Task<T>
detiene in modo ordinato un calcolo "ha iniziato, potrebbe essere finito", che può essere composto con altre attività, mappato con funzioni, ecc. Al contrario, la monade F # async
contiene un calcolo "potrebbe iniziare più tardi, potrebbe essere in esecuzione ora" , insieme a a CancellationToken
. In C#, in genere è necessario collegare il CancellationToken
a tutte le funzioni che funzionano con Task
. Perché il team C# ha scelto di completare il calcolo nella monade Task
, ma non nello CancellationToken
?Perché non è incluso un CancelToken nell'attività <T> monad?
risposta
Più o meno, hanno incapsulato l'uso implicito di CancellationToken
per i metodi C# async
. Considerate questo:
var cts = new CancellationTokenSource();
cts.Cancel();
var token = cts.token;
var task1 = new Task(() => token.ThrowIfCancellationRequested());
task1.Start();
task1.Wait(); // task in Faulted state
var task2 = new Task(() => token.ThrowIfCancellationRequested(), token);
task2.Start();
task2.Wait(); // task in Cancelled state
var task3 = (new Func<Task>(async() => token.ThrowIfCancellationRequested()))();
task3.Wait(); // task in Cancelled state
per un non-async lambda, ho dovuto associare esplicitamente token
con la task2
per l'annullamento di propagare correttamente, fornendo come argomento per new Task()
(o Task.Run
). Per un lambda async
utilizzato con task3
, si verifica automaticamente come parte del codice dell'infrastruttura async/await
.
Inoltre, qualsiasitoken
si propaga cancellazione di un metodo async
, mentre per non asincrone computazionale new Task()
/Task.Run
lambda deve essere token stesso passato al costruttore compito o Task.Run
.
Ovviamente, dobbiamo ancora chiamare token.ThrowIfCancellationRequested()
manualmente per implementare il modello di annullamento della cooperativa. Non riesco a capire perché i team C# e TPL abbiano deciso di implementarlo in questo modo, ma immagino che mirassero a non complicare eccessivamente la sintassi di async/await
ma mantenerlo sufficientemente flessibile.
Per quanto riguarda F #, non ho esaminato il codice IL generato del flusso di lavoro asincrono, illustrato in blog post di Tomas Petricek collegato. Tuttavia, per quanto ho capito, il token viene automaticamente testato solo in alcune posizioni del flusso di lavoro, quelle corrispondenti a await
in C# (per analogia, potremmo chiamare token.ThrowIfCancellationRequested()
manualmente dopo ogni await
in C#). Ciò significa che qualsiasi lavoro associato alla CPU non verrà annullato immediatamente. In caso contrario, F # dovrebbe emettere token.ThrowIfCancellationRequested()
dopo ogni istruzione IL, che sarebbe un notevole overhead.
Quando e perché si desidera utilizzare async/attendere il lavoro con CPU? – GregC
@GregC, ogni volta che devo eseguire il lavoro con CPU in un'app dell'interfaccia utente: 'var pi = attendi Task.Run (() => CalcPi (cifre, token), token)'. – Noseratio
@GregC Come forma di parallelizzazione. 'aspetta Task.WhenAll (tasks)'. – Aron
Inizialmente è stata eseguita un'attività per contenere funzionalità aggiuntive nella classe, ma in seguito è stata modificata per aggregare un oggetto con supporto di funzionalità aggiuntive. Tutto nel nome della performance.
http://blogs.msdn.com/b/pfxteam/archive/2011/11/10/10235962.aspx (vedere "Ristrutturazione Attività" nel documento) Il documento di Joseph E. Hoag fornisce interessanti approfondimenti sulle ottimizzazioni fatte in .NET 4.5. Credo che sarebbe una lettura utile per chiunque cerchi di spremere l'ultimo 10% delle prestazioni da asincrono/attesa.
Presumo che un processo di pensiero simile è stato applicato quando si decide come pacchettizzare la funzionalità di cancellazione.
Non riesco a parlare per il team C# o BCL, ma presumo che si tratti di un ottimizzazione delle prestazioni che è possibile solo nel compilatore F #, o che le prestazioni non erano rilevanti per il team F #. SRP, baby!
Mi ero dimenticato di quella carta Hoag. Sembra che il drive fosse mantenere l'oggetto Task il più piccolo possibile, quindi non portarsi dietro il token di cancellazione, ma invece portarlo in giro nella macchina a stati generata da async/await. –
- 1. Come utilizzare l'espressione del generatore CMake $ <TARGET_FILE: tgt>?
- 2. Perché non è `parte join` del` Monad` classe
- 3. Perché Tuple non ha un'istanza Monad?
- 4. Perché MonadPlus e non Monad + Monoid?
- 5. Perché non c'è nessun << nella libreria standard Haskell?
- 6. Perché <deny users = "?" /> incluso nel seguente esempio?
- 7. Perché non è incluso questo file di intestazione di boost
- 8. Come scoprire perché un barattolo è stato incluso da Maven?
- 9. Trasformatori Monad Duplicazione Monad
- 10. Perché <br> è un elemento, non un'entità?
- 11. Perché questo codice E-monad non digita il controllo?
- 12. Perché non esiste un operatore << per std :: unique_ptr?
- 13. classe PHP non trovato, ma è incluso
- 14. Perché l'operazione << su un array in Ruby non è atomica?
- 15. mysql - Perché "n" è incluso quando cerco "ñ"?
- 16. Perché non è Array.newInstance (Classe <?>, int) generica?
- 17. Perché std :: packaged_task <void()> non è valido?
- 18. Monad transformer per NonEmptyList?
- 19. Perché l'attività <T> non è co-variante?
- 20. Perché Nullable <T> non è un parametro di attributo personalizzato valido quando T è?
- 21. JS Funzione unità Monad
- 22. Perché il tipo di ritorno del metodo non è incluso nella firma del metodo?
- 23. Perché il predicato <> è sigillato?
- 24. Perché JavaFX non è incluso in OpenJDK 8 su Ubuntu Wily (15.10)?
- 25. Perché l'assembly System.Configuration non è incluso nei nuovi progetti per impostazione predefinita?
- 26. Perché IDEA non risolve scala.reflect, ma scala-reflect è incluso nelle impostazioni del progetto?
- 27. Perché non posso usare localStorage nel mio .config quando è incluso nella definizione di .module?
- 28. Caricamento di un pennino che è incluso in un quadro
- 29. È permgen incluso in -Xmx?
- 30. Perché <! - Non genera un errore di sintassi?
In C#, è possibile eseguire il calcolo del * "potrebbe iniziare in un secondo momento, potrebbe essere in esecuzione ora", insieme a un'opzione CancelToken * sotto forma di 'var task = new Task (action, token)'. – Noseratio