web-dev-qa-db-fra.com

TaskCompletionSource - Essayer de comprendre le travail asynchrone sans fil

J'essaie de comprendre le but de TaskCompletionSource et sa relation avec le travail asynchrone/sans fil. Je pense que j'ai l'idée générale mais je veux m'assurer que ma compréhension est correcte.

J'ai d'abord commencé à examiner la bibliothèque parallèle de tâches (TPL) pour déterminer s'il y avait un bon moyen de créer votre propre travail sans fil/asynchrone (par exemple, vous essayez d'améliorer l'évolutivité de votre site ASP.NET), ainsi que la compréhension du TPL semble que ce sera très important à l'avenir (async/await). Ce qui m'a conduit au TaskCompletionSource.

D'après ce que je comprends, il semble que l'ajout de TaskCompletionSource à l'une de vos classes ne fasse pas grand-chose pour rendre votre codage asynchrone; si vous exécutez toujours du code de synchronisation, l'appel à votre code se bloquera. Je pense que cela est même vrai pour les API Microsoft. Par exemple, disons dans DownloadStringTaskAsync hors de la classe WebClient, tout code de configuration/synchronisation qu'ils effectuent initialement sera bloqué. Le code que vous exécutez doit s'exécuter sur un thread, soit le thread actuel, soit vous devrez en créer un nouveau.

Vous utilisez donc TaskCompletionSource dans votre propre code lorsque vous appelez d'autres appels async de Microsoft afin que le client de vos classes n'ait pas à créer un nouveau thread pour que votre classe ne bloque pas.

Je ne sais pas comment Microsoft fait ses API asynchrones en interne. Par exemple, il existe une nouvelle méthode async en dehors de SqlDataReader pour .Net 4.5. Je sais qu'il y a IO Ports d'achèvement. Je pense que c'est une abstraction de niveau inférieur (C++?) Que probablement la plupart des développeurs C # n'utiliseront pas. Je ne sais pas si IO = achèvement Les ports fonctionneront pour la base de données ou les appels réseau (HTTP) ou s'ils sont juste utilisés pour le fichier IO.

Donc la question est, est-ce que j'ai raison de comprendre? Y a-t-il certaines choses que j'ai mal représentées?

35
coding4fun

TaskCompletionSource est utilisé pour créer des objets Task qui n'exécutent pas de code.

Ils sont beaucoup utilisés par les nouvelles API asynchrones de Microsoft - chaque fois qu'il y a des opérations asynchrones basées sur les E/S (ou d'autres opérations asynchrones non basées sur le processeur, comme un délai d'expiration). En outre, tout async Task la méthode que vous écrivez utilisera TCS pour terminer son Task retourné.

J'ai un article de blog Création de tâches qui explique différentes façons de créer des instances Task. Il est écrit dans une perspective async/await (pas une perspective TPL), mais il s'applique toujours ici.

Voir également les excellents articles de Stephen Toub:

59
Stephen Cleary

J'aime l'explication qui a été fournie dans http://tutorials.csharp-online.net/TaskCompletionSource

(désolé, le lien est peut-être mort pour le moment)

Les deux premiers paragraphes sont ci-dessous

Nous avons vu comment Task.Run crée une tâche qui exécute un délégué sur un thread groupé (ou non groupé). Une autre façon de créer une tâche est avec TaskCompletionSource.

TaskCompletionSource vous permet de créer une tâche à partir de toute opération qui démarre et se termine un certain temps plus tard. Il fonctionne en vous donnant une tâche "esclave" que vous conduisez manuellement - en indiquant quand l'opération se termine ou en cas de défaut. Ceci est idéal pour le travail lié aux E/S: vous obtenez tous les avantages des tâches (avec leur capacité à propager des valeurs de retour, des exceptions et des continuations) sans bloquer un thread pendant la durée de l'opération.

Pour utiliser TaskCompletionSource, vous instanciez simplement la classe. Il expose une propriété Task qui renvoie une tâche sur laquelle vous pouvez attendre et attacher des continuations, comme pour toute autre tâche. Cependant, la tâche est entièrement contrôlée par l'objet TaskCompletionSource via les méthodes suivantes:

public class TaskCompletionSource<TResult> 
{ 
 public void SetResult(TResult result); 
 public void SetException (Exception exception); 

 public void SetCanceled();   
 public bool TrySetResult (TResult result); 
 public bool TrySetException (Exception exception); 
 public bool TrySetCanceled();
 ... 
}

L'appel de l'une de ces méthodes signale la tâche, la mettant dans un état terminé, défectueux ou annulé (nous couvrirons ce dernier dans la section "Annulation"). Vous êtes censé appeler une seule de ces méthodes exactement une fois: si elle est appelée à nouveau, SetResult, SetException ou SetCanceled lèvera une exception, tandis que les méthodes Try * renvoient false.

L'exemple suivant imprime 42 après avoir attendu cinq secondes:

var tcs = new TaskCompletionSource<int>();
new Thread (() =>     {
                       Thread.Sleep (5000); 
                       tcs.SetResult (42); 
                      })    
           .Start();   
Task<int> task = tcs.Task;    // Our "slave" task. 
Console.WriteLine(task.Result);  // 42

Autres citations intéressantes

La véritable puissance de TaskCompletionSource réside dans la création de tâches qui ne bloquent pas les threads.

.. et plus tard

Notre utilisation de TaskCompletionSource sans thread signifie qu'un thread n'est engagé que lorsque la poursuite démarre, cinq secondes plus tard. Nous pouvons le démontrer en démarrant 10 000 de ces opérations à la fois sans erreur ni consommation excessive de ressources:

6
Maxim Eliseev