Exécuter une fonction async dans un autre thread

j'évalue le CTP Async.

Comment puis-je commencer l'exécution d'une fonction async sur le thread d'un autre pool de threads?

static async Task Test()
{
    // Do something, await something
}

static void Main( string[] args )
{
    // Is there more elegant way to write the line below?
    var t = TaskEx.Run( () => Test().Wait() );

    // Doing much more in this same thread
    t.Wait(); // Waiting for much more then just this single task, this is just an example
}
14
demandé sur Soonts 2011-02-11 22:52:39

3 réponses

je suis nouveau (mon virginale post) à Débordement de Pile, mais je suis d'illusions que vous vous posez à propos de la Async CTP depuis que je suis sur l'équipe travaille chez Microsoft :)

je pense que je comprends ce que vous visez, et il y a quelques choses que vous faites correctement, pour vous y rendre.

Ce que je pense que vous voulez:

static async Task Test()
{
    // Do something, await something
}

static void Main(string[] args)
{
    // In the CTP, use Task.RunEx(...) to run an Async Method or Async Lambda
    // on the .NET thread pool
    var t = TaskEx.RunEx(Test);
    // the above was just shorthand for
    var t = TaskEx.RunEx(new Func<Task>(Test));
    // because the C# auto-wraps methods into delegates for you.

    // Doing much more in this same thread
    t.Wait(); // Waiting for much more then just this single task, this is just an example
}

tâche.Run vs Tâche.RunEx 1519270920"

parce que ce CTP s'installe sur .NET 4.0, nous ne voulions pas patcher le type System.Threading.Tasks.Task dans mscorlib. Au lieu de cela, les APIs de terrain de jeu sont nommés FooEx quand ils sont en conflit.

Pourquoi avons-nous nommé certains d'entre eux Run(...) et certains des RunEx(...) ? La raison en est que nous n'avions pas encore terminé la refonte de la méthode au moment où nous avons publié le CTP. Dans notre base de données actuelle, nous avons en fait, j'ai dû modifier légèrement les règles de surcharge de la méthode C# afin que la bonne chose arrive pour Async Lambdas - qui peut retourner void , Task , ou Task<T> .

le problème est que lorsque la méthode async ou lambdas retourne Task ou Task<T> , ils n'ont en fait pas le type de tâche externe dans l'expression de retour, parce que la tâche est générée automatiquement pour vous dans le cadre de la méthode ou de l'invocation de lambda. Cela nous semble fortement comme le bonne expérience pour la clarté du code, bien que cela ne rend les choses tout à fait différent avant, puisque typiquement l'expression des déclarations de retour est directement convertible au type de retour de la méthode ou lambda.

ainsi donc, à la fois async void lambdas et async Task lambdas support return; sans arguments. D'où la nécessité d'une clarification de la méthode de résolution de surcharge de décider lequel choisir. Ainsi, la seule raison pour laquelle vous avez Exécuté(...) et RunEx(...) était pour que nous nous assurions d'avoir un support de meilleure qualité pour les autres parties du CTP asynchrone, d'ici au moment du succès de PDC 2010.


Comment penser les méthodes asynchrones/lambdas

Je ne suis pas sûr que ce soit un point de confusion, mais j'ai pensé que je voudrais le mentionner - quand vous écrivez une méthode async ou async lambda, il peut prendre certaines caractéristiques de celui qui l'invoque. C'est fondé sur deux choses:

  • le type sur lequel vous attendez
  • et éventuellement le contexte de synchronisation (selon ci-dessus)

la conception CTP pour l'attente et notre design interne actuel sont tous deux très basés sur le modèle de sorte que les fournisseurs D'API peuvent aider à étoffer un ensemble dynamique de choses que vous pouvez "attendre" sur. Cela peut varier en fonction du type sur lequel vous attendez, et le type commun pour cela est Task .

Task 's attendent la mise en œuvre est très raisonnable, et s'en remet au thread en cours SynchronizationContext pour décider de la façon de reporter les travaux. Dans le cas où vous êtes déjà dans une boucle de message WinForms ou WPF, alors votre exécution différée reviendra sur la même boucle de message (comme si vous aviez utilisé BeginInvoke() le "reste de votre méthode"). Si vous attendez une tâche et que vous êtes déjà sur le threadpool. net, alors le "reste de votre méthode" reprendra sur l'un des threadpool threads (mais pas nécessairement le même exactement), car ils ont été mis en commun pour commencer et très probablement vous êtes heureux d'aller avec le premier pool thread disponible.


mise en garde concernant L'utilisation des méthodes Wait ()

dans l'échantillon que vous avez utilisé: var t = TaskEx.Run( () => Test().Wait() );

Ce qui n'est:

  1. dans le fil environnant appel synchrone TaskEx.Exécuter.(..) pour exécuter une lambda sur le pool de thread.
  2. un filetage de piscine est désigné pour la lambda, et il invoque votre méthode async.
  3. La méthode async Test() est appelée à partir de la lambda. Parce que la lambda exécutait sur le pool de threads, toutes les continuations à L'intérieur de Test() peuvent s'exécuter sur n'importe quel threads dans le pool de threads.
  4. la lambda ne libère pas réellement la pile de ce fil parce qu'elle n'y avait pas d'attente. Le Le comportement de TPL dans ce cas dépend de Si Test() a réellement terminé avant L'appel Wait (). Cependant, dans ce cas, il y a une réelle possibilité que vous bloquiez un thread pool pendant qu'il attend Test() pour terminer l'exécution sur un thread différent.

C'est le principal avantage de l'opérateur 'wait' est qu'il vous permet d'ajouter du code qui s'exécute plus tard - mais sans bloquer le thread original. Dans le cas de la piscine de fil, vous pouvez obtenir fil de l'utilisation.

Laissez-moi savoir si vous avez d'autres questions au sujet de la Async CTP de visual basic ou C#, je serais ravi de les entendre :)

51
répondu Theo Yaung 2011-05-22 04:37:17

c'est habituellement à la méthode de retourner le Task de déterminer où il court, si elle commence vraiment nouveau travail au lieu de juste piggy-Back sur quelque chose d'autre.

dans ce cas, il ne ressemble pas à vous vraiment veulent que la méthode Test() d'être asynchrone - au moins, vous n'êtes pas en utilisant le fait qu'il est asynchrone. Tu commences juste des trucs dans un autre fil... la méthode Test() pourrait être entièrement synchrone, et vous pouvez simplement utiliser:

Task task = TaskEx.Run(Test);
// Do stuff
t.Wait();

qui ne nécessite aucune de la bonté async CTP.

5
répondu Jon Skeet 2011-02-11 20:07:24

il y en aurait, si ce n'était pas une application console. Par exemple, si vous faites cela dans une application Windows Forms, vous pouvez faire:

// Added to a button click event, for example
public async void button1_Click(object sender, EventArgs e)
{
    // Do some stuff
    await Test();
    // Do some more stuff
}

cependant, il n'y a pas de défaut SynchronizationContext dans une console, donc cela ne fonctionnera pas de la façon que vous attendez. Dans une application console, vous devez explicitement saisir la tâche, puis d'attendre à la fin.

si vous faites cela dans un thread D'UI dans les formes de Windows, WPF, ou même un service de WCF, il y aura un le contexte de synchronisation valide qui sera utilisé pour marshal back les résultats correctement. Dans une application console, cependant, lorsque la commande est "retournée" à l'appel await , le programme continue, et se termine immédiatement. Cela a tendance à tout gâcher, et à produire un comportement inattendu.

2
répondu Reed Copsey 2011-02-11 19:58:16