2013-03-12 9 views
12

Ho provato ad utilizzare il metodo SwitchTo oggi per passare al thread GUI e ho scoperto che l'esempio da cui l'ho generato non funziona, semplicemente perché il metodo non è presente.Perché lo switch "SwitchTo" è stato rimosso da Async CTP/Release?

Poi ho trovato questo trafiletto here:

La ragione ci siamo sbarazzati di che era perché era così pericoloso. L'alternativa è di impacchettare il codice all'interno TaskEx.Run ...

La mia domanda è semplice: perché era pericoloso? Quali sono i pericoli specifici che l'avrebbero utilizzata?

Si noti che I ha fatto leggere il resto di quel post, quindi capisco ci sono limitazioni tecniche qui. La mia domanda è ancora, se sono a conoscenza di questo, perché è pericoloso?

Sto pensando di reimplementare metodi di supporto per darmi la funzionalità specificata, ma se c'è qualcosa di fondamentalmente rotto, a parte che qualcuno ha deciso che era pericoloso, non lo farei.

In particolare, molto ingenuamente, ecco come vorrei prendere in considerazione l'attuazione dei metodi richiesti:

public static class ContextSwitcher 
{ 
    public static ThreadPoolContextSwitcher SwitchToThreadPool() 
    { 
     return new ThreadPoolContextSwitcher(); 
    } 

    public static SynchronizationContextSwitcher SwitchTo(this SynchronizationContext synchronizationContext) 
    { 
     return new SynchronizationContextSwitcher(synchronizationContext); 
    } 
} 

public class SynchronizationContextSwitcher : INotifyCompletion 
{ 
    private readonly SynchronizationContext _SynchronizationContext; 

    public SynchronizationContextSwitcher(SynchronizationContext synchronizationContext) 
    { 
     _SynchronizationContext = synchronizationContext; 
    } 

    public SynchronizationContextSwitcher GetAwaiter() 
    { 
     return this; 
    } 

    public bool IsCompleted 
    { 
     get 
     { 
      return false; 
     } 
    } 

    public void OnCompleted(Action action) 
    { 
     _SynchronizationContext.Post(_ => action(), null); 
    } 

    public void GetResult() 
    { 
    } 
} 

public class ThreadPoolContextSwitcher : INotifyCompletion 
{ 
    public ThreadPoolContextSwitcher GetAwaiter() 
    { 
     return this; 
    } 

    public bool IsCompleted 
    { 
     get 
     { 
      return false; 
     } 
    } 

    public void OnCompleted(Action action) 
    { 
     ThreadPool.QueueUserWorkItem(_ => action(), null); 
    } 

    public void GetResult() 
    { 
    } 
} 

Questo mi avrebbe permesso di scrivere codice in questo modo:

public async void Test() 
{ 
    await ContextSwitcher.SwitchToThreadPool(); // ensure we're not bogging down the UI thread 
    // do some heavy processing 
    await _UIContext.SwitchTo(); // presumably saved from the main thread 
    // update UI with new data 
} 
+0

Hah.Questo è piuttosto un vecchio thread! Non sono mai stato un fan del ragionamento occasionale di Microsoft "è per il tuo bene". –

+1

Da allora sono passato a fare 'Attendere Task.Run (async() => {})' - non per evitare alcuni pericoli vuoti, ma semplicemente perché penso che sia più facile da leggere. Penso che la tua idea di come implementare 'SwitchTo()' sia valida, comunque. –

+0

Non conoscevo la sintassi 'async() => {}', ha bisogno di ulteriori indagini, grazie! –

risposta

6

Stephen Toub ha qualche informazione in più su il ragionamento in this thread.

In sintesi, non è una buona idea per due motivi:

  1. Promuove il codice non strutturati. Se hai "elaborazione pesante" che devi fare, deve essere inserito in un Task.Run. Ancora meglio, separa la logica aziendale dalla logica dell'interfaccia utente.
  2. Gestione degli errori e (alcuni) continuazioni eseguiti in un contesto sconosciuto. catch/finally blocchi in Test dovrebbero gestire l'esecuzione in un pool di thread o contesto UI (e se sono in esecuzione nel contesto del pool di thread, non è possibile utilizzare SwitchTo per saltare nel contesto dell'interfaccia utente). Inoltre, fino a quando si await restituita Task si dovrebbe essere OK (await correggerà contesto prosecuzione, se necessario), ma se avete esplicite ContinueWith continuazioni che utilizzano ExecuteSynchronously, quindi avranno lo stesso problema come i catch/finally blocchi .

In breve, il codice è più pulito e più prevedibile senza SwitchTo.

+0

Ok, questo ci illumina di più. Sembra ancora strano, quel post, per dire che crea tutti i tipi di problemi con contesti sconosciuti, e poi passa a mostrare comunque come implementare il supporto. –

+0

@ LasseV.Karlsen Penso che questo atteggiamento non sia raro per gli sviluppatori di .Net. A quanto ho capito, stanno dicendo qualcosa del tipo "Se fornissimo questa funzione, la gente penserebbe che sia giusto usarla, quindi la userebbero spesso, il che è una brutta cosa. Ma se è pubblicato su alcuni blog, è probabile che lo usino solo quando ne hanno davvero bisogno e capiscono cosa sta succedendo, che va bene. " – svick

+0

@svick Posso accettarlo :) –

2

ConfigureAwait è in realtà più pericoloso di SwitchTo. Tracciare mentalmente il contesto corrente e l'ultima chiamata SwitchTo non è più difficile del tracciamento a cui è stata assegnata l'ultima variabile. D'altra parte, ConfigureAwait cambia contesto se e solo se la chiamata viene effettivamente eseguita in modo asincrono. Se l'attività è stata già completata, il contesto viene mantenuto. Non hai il controllo su questo.

Problemi correlati