Les nouveaux mots-clés «asynchrones» et «attendus» C # 5.0 utilisent-ils plusieurs cœurs?

Deux nouveaux mots-clés ajoutés au langage C # 5.0 sont asynchrones et en attente , les deux fonctionnent de concert pour exécuter une méthode C # de manière asynchrone sans bloquer le thread appelant.

Ma question est la suivante: ces méthodes utilisent-elles réellement plusieurs cœurs et s’exécutent-elles en parallèle ou la méthode asynchrone est-elle exécutée dans le même cœur de thread que l’appelant?

Deux nouveaux mots-clés ajoutés au langage C # 5.0 sont asynchrones et en attente, les deux fonctionnent de concert pour exécuter une méthode C # de manière asynchrone sans bloquer le thread appelant.

Cela couvre l’ objective de la fonctionnalité, mais cela donne trop de “crédit” à la fonctionnalité asynchrone / d’attente.

Laissez-moi être très, très clair sur ce point: await ne fait pas comme par magie une méthode synchrone pour s’exécuter de manière asynchrone. Il ne démarre pas un nouveau thread et lance la méthode sur le nouveau thread, par exemple. La méthode que vous appelez doit être la chose qui sait s’exécuter de manière asynchrone. La façon dont il choisit de le faire est son affaire.

Ma question est la suivante: ces méthodes utilisent-elles réellement plusieurs cœurs et s’exécutent-elles en parallèle ou la méthode asynchrone est-elle exécutée dans le même cœur de thread que l’appelant?

Encore une fois, cela dépend entièrement de la méthode que vous appelez . Tout ce qui await est de demander au compilateur de réécrire la méthode en un délégué qui peut être transmis comme la poursuite de la tâche asynchrone. Autrement dit, await FooAsync() signifie “appelez FooAsync() et tout ce qui revient doit représenter l’opération asynchrone qui vient juste de démarrer. Dites-lui que lorsqu’il sait que l’opération asynchrone est terminée, il doit appeler ce délégué . ” Le délégué a la propriété que, lorsqu’il est appelé, la méthode en cours semble reprendre “là où elle s’est arrêtée”.

Si la méthode que vous appelez fonctionne sur un autre thread associé à un autre core, génial. Si elle lance un minuteur qui envoie des commandes à un gestionnaire d’événements à l’avenir sur le thread d’interface utilisateur, c’est bien. await ne se soucie pas. Tout ce qu’il fait est de s’assurer que lorsque le travail asynchrone est terminé, le contrôle peut reprendre là où il s’est arrêté.

Une question que vous n’avez pas posée mais que vous devriez probablement poser est la suivante:

Lorsque la tâche asynchrone est terminée et que le contrôle reprend là où elle s’est arrêtée, l’exécution est-elle dans le même thread qu’avant?

Ça dépend du contexte. Dans une application winforms où vous attendez quelque chose du thread d’interface utilisateur, le contrôle reprend sur le thread d’interface utilisateur. Dans une application console, peut-être pas.

Eric Lippert a une excellente réponse. Je voulais juste décrire le parallélisme async un peu plus loin.

L’approche simple “série” est l’endroit où vous await qu’une chose à la fois:

 static void Process() { Thread.Sleep(100); // Do CPU work. } static async Task Test() { await Task.Run(Process); await Task.Run(Process); } 

Dans cet exemple, la méthode Test mettra en queue Process au pool de threads et, une fois cette opération terminée, mettra à nouveau en attente le Process dans le pool de threads. La méthode de Test se terminera après environ 200 ms. À tout moment, un seul thread fait réellement avancer les choses.

Un moyen simple de paralléliser ceci est d’utiliser Task.WhenAll :

 static void Process() { Thread.Sleep(100); // Do CPU work. } static async Task Test() { // Start two background operations. Task task1 = Task.Run(Process); Task task2 = Task.Run(Process); // Wait for them both to complete. await Task.WhenAll(task1, task2); } 

Dans cet exemple, la méthode Test queue le Process sur le pool de threads, puis attend leur finalisation. La méthode de Test se terminera après environ 100 ms.

Task.WhenAll (et Task.WhenAny ) ont été introduits avec async / await pour prendre en charge le parallélisme simple. Cependant, le TPL est toujours là si vous avez besoin de quelque chose de plus avancé (le vrai parallel processing lié au processeur est plus adapté au TPL). TPL joue bien avec l’ async / await .

Je couvre le parallélisme async base dans mon blog async , ainsi que le “contexte” auquel Eric a fait allusion.

Une méthode asynchrone renvoie un object attendu (celui qui a une méthode GetAwaiter ), et le compilateur peut générer du code pour consumr cet object si vous appelez la méthode avec le mot-clé await . Vous êtes également libre d’appeler une telle méthode sans le mot-clé wait et consumz l’object explicitement.

L’object encapsule une action asynchrone, qui peut ou peut ne pas s’exécuter sur un autre thread. L’article d’ Aric Lippert Asynchrony in C # 5.0 part Four: Ce n’est pas de la magie que de considérer un exemple de programmation asynchrone qui implique un seul thread.

Comme l’ async et l’ await sont basées sur la TPL, elles devraient fonctionner de manière très similaire. Par défaut, vous devriez les traiter comme s’ils s’exécutaient sur un thread séparé.