Je pensais que c'était essentiellement la même chose - écrire des programmes qui répartissent les tâches entre les processeurs (sur les machines qui ont 2+ processeurs). Alors je lis ceci , qui dit:
Les méthodes asynchrones sont destinées à être des opérations non bloquantes. Une expression d'attente dans une méthode async ne bloque pas le thread actuel pendant l'exécution de la tâche attendue. Au lieu de cela, l'expression signe le reste de la méthode en tant que continuation et renvoie le contrôle à l'appelant de la méthode async.
Les mots clés asynchrones et en attente n'entraînent pas la création de threads supplémentaires. Les méthodes asynchrones ne nécessitent pas de multithreading car une méthode asynchrone ne s'exécute pas sur son propre thread. La méthode s'exécute sur le contexte de synchronisation actuel et utilise l'heure sur le thread uniquement lorsque la méthode est active. Vous pouvez utiliser Task.Run pour déplacer le travail lié au processeur vers un thread d'arrière-plan, mais un thread d'arrière-plan n'aide pas avec un processus qui attend simplement que les résultats soient disponibles.
et je me demande si quelqu'un peut traduire cela en anglais pour moi. Il semble faire une distinction entre l'asyncronicité (est-ce un mot?) Et le threading et impliquer que vous pouvez avoir un programme qui a des tâches asynchrones mais pas de multithreading.
Maintenant, je comprends l'idée de tâches asynchrones comme l'exemple sur pg. 467 de C # In Depth de Jon Skeet , troisième édition
async void DisplayWebsiteLength ( object sender, EventArgs e )
{
label.Text = "Fetching ...";
using ( HttpClient client = new HttpClient() )
{
Task<string> task = client.GetStringAsync("http://csharpindepth.com");
string text = await task;
label.Text = text.Length.ToString();
}
}
Le async
mot-clé signifie " Cette fonction, chaque fois qu'elle est appelée, ne sera pas appelée dans un contexte dans lequel son achèvement est requis pour tout après son appel."
En d'autres termes, l'écrire au milieu d'une tâche
int x = 5;
DisplayWebsiteLength();
double y = Math.Pow((double)x,2000.0);
, car DisplayWebsiteLength()
n'a rien à voir avec x
ou y
, entraînera DisplayWebsiteLength()
l'exécution "en arrière-plan", comme
processor 1 | processor 2
-------------------------------------------------------------------
int x = 5; | DisplayWebsiteLength()
double y = Math.Pow((double)x,2000.0); |
Évidemment, c'est un exemple stupide, mais ai-je raison ou suis-je totalement confus ou quoi?
(En outre, je ne comprends pas pourquoi sender
et e
ne sont jamais utilisés dans le corps de la fonction ci-dessus.)
la source
sender
ete
suggèrent qu'il s'agit en fait d'un gestionnaire d'événements - à peu près le seul endroit où celaasync void
est souhaitable. Très probablement, cela s'appelle sur un clic de bouton ou quelque chose comme ça - le résultat étant que cette action se produit de manière complètement asynchrone par rapport au reste de l'application. Mais tout est toujours sur un thread - le thread d'interface utilisateur (avec un minuscule ruban de temps sur un thread IOCP qui publie le rappel sur le thread d'interface utilisateur).DisplayWebsiteLength
exemple de code: vous ne devez pas utiliserHttpClient
dans uneusing
instruction - sous une charge élevée, le code peut épuiser le nombre de sockets disponibles, ce qui entraîne des erreurs SocketException. Plus d'informations sur l' instanciation incorrecte .Réponses:
Votre malentendu est extrêmement courant. Beaucoup de gens apprennent que le multithreading et l'asynchronie sont la même chose, mais ils ne le sont pas.
Une analogie aide généralement. Vous cuisinez dans un restaurant. Une commande arrive pour les œufs et les toasts.
Est-il maintenant logique que le multithreading ne soit qu'un type d'asynchronie? Le filetage concerne les travailleurs; l'asynchronie concerne les tâches . Dans les workflows multithreads, vous affectez des tâches aux travailleurs. Dans les workflows asynchrones à un seul thread, vous avez un graphique des tâches où certaines tâches dépendent des résultats d'autres; à mesure que chaque tâche se termine, elle invoque le code qui planifie la prochaine tâche qui peut s'exécuter, compte tenu des résultats de la tâche qui vient d'être terminée. Mais (espérons-le) vous n'avez besoin que d'un seul travailleur pour effectuer toutes les tâches, pas d'un seul travailleur par tâche.
Cela vous aidera à réaliser que de nombreuses tâches ne sont pas liées au processeur. Pour les tâches liées au processeur, il est logique d'embaucher autant de travailleurs (threads) qu'il y a de processeurs, d'affecter une tâche à chaque travailleur, d'affecter un processeur à chaque travailleur et de faire en sorte que chaque processeur ne fasse rien d'autre que de calculer le résultat comme Aussi vite que possible. Mais pour les tâches qui n'attendent pas sur un processeur, vous n'avez pas du tout besoin d'affecter un travailleur. Vous attendez simplement que le message arrive que le résultat est disponible et faites autre chose pendant que vous attendez . Lorsque ce message arrive, vous pouvez planifier la poursuite de la tâche terminée en tant que prochaine chose sur votre liste de tâches à cocher.
Examinons donc l'exemple de Jon plus en détail. Ce qui se produit?
text
et exécuter le reste de la méthode.C'est comme dans mon analogie. Quelqu'un vous demande un document. Vous envoyez par la poste le document et continuez à faire d'autres travaux. Quand il arrive dans le courrier, vous êtes signalé et quand vous en avez envie, vous effectuez le reste du flux de travail - ouvrez l'enveloppe, payez les frais de livraison, peu importe. Vous n'avez pas besoin d'embaucher un autre travailleur pour faire tout cela pour vous.
la source
Javascript dans le navigateur est un excellent exemple d'un programme asynchrone qui n'a pas de threads.
Vous n'avez pas à vous soucier que plusieurs morceaux de code touchent les mêmes objets en même temps: chaque fonction finira de s'exécuter avant que tout autre javascript ne soit autorisé à s'exécuter sur la page.
Cependant, lorsque vous faites quelque chose comme une demande AJAX, aucun code n'est en cours d'exécution, donc d'autres javascript peuvent répondre à des choses comme les événements de clic jusqu'à ce que cette demande revienne et invoque le rappel qui lui est associé. Si l'un de ces autres gestionnaires d'événements est toujours en cours d'exécution lorsque la demande AJAX est renvoyée, son gestionnaire ne sera pas appelé avant d'avoir terminé. Il n'y a qu'un "thread" JavaScript en cours d'exécution, même s'il vous est possible de suspendre efficacement la chose que vous faisiez jusqu'à ce que vous ayez les informations dont vous avez besoin.
Dans les applications C #, la même chose se produit chaque fois que vous traitez avec des éléments d'interface utilisateur - vous n'êtes autorisé à interagir avec des éléments d'interface utilisateur que lorsque vous êtes sur le thread d'interface utilisateur. Si l'utilisateur cliquait sur un bouton et que vous vouliez répondre en lisant un fichier volumineux sur le disque, un programmeur inexpérimenté pourrait commettre l'erreur de lire le fichier dans le gestionnaire d'événements Click lui-même, ce qui entraînerait le gel de l'application jusqu'à ce que le le chargement du fichier est terminé, car il n'est plus autorisé à répondre aux clics, au survol ou à tout autre événement lié à l'interface utilisateur tant que ce thread n'est pas libéré.
Une option que les programmeurs peuvent utiliser pour éviter ce problème est de créer un nouveau thread pour charger le fichier, puis d'indiquer au code de ce thread que lorsque le fichier est chargé, il doit exécuter à nouveau le code restant sur le thread d'interface utilisateur afin qu'il puisse mettre à jour les éléments d'interface utilisateur en fonction de ce qu'il a trouvé dans le fichier. Jusqu'à récemment, cette approche était très populaire car c'était ce que les bibliothèques et le langage C # facilitaient, mais c'est fondamentalement plus compliqué qu'il ne doit l'être.
Si vous pensez à ce que le CPU fait quand il lit un fichier au niveau du matériel et du système d'exploitation, il émet essentiellement une instruction pour lire les données du disque dans la mémoire et pour frapper le système d'exploitation avec une "interruption" "lorsque la lecture est terminée. En d'autres termes, la lecture à partir du disque (ou de toute E / S en réalité) est une opération intrinsèquement asynchrone . Le concept d'un thread en attente de fin d'E / S est une abstraction que les développeurs de la bibliothèque ont créée pour faciliter la programmation. Ce n'est pas nécessaire.
Maintenant, la plupart des opérations d'E / S dans .NET ont une
...Async()
méthode correspondante que vous pouvez invoquer, qui retourneTask
presque immédiatement. Vous pouvez ajouter des rappels à celaTask
pour spécifier le code que vous souhaitez exécuter à la fin de l'opération asynchrone. Vous pouvez également spécifier sur quel thread vous souhaitez que ce code s'exécute et vous pouvez fournir un jeton que l'opération asynchrone peut vérifier de temps en temps pour voir si vous avez décidé d'annuler la tâche asynchrone, ce qui lui donne la possibilité d'arrêter son travail rapidement et gracieusement.Jusqu'à ce que les
async/await
mots clés soient ajoutés, C # était beaucoup plus évident sur la façon dont le code de rappel était invoqué, car ces rappels étaient sous la forme de délégués que vous avez associés à la tâche. Afin de vous donner toujours l'avantage d'utiliser l'...Async()
opération, tout en évitant la complexité du code,async/await
résume la création de ces délégués. Mais ils sont toujours là dans le code compilé.Ainsi, vous pouvez faire en sorte que votre gestionnaire d'événements d'interface utilisateur effectue
await
une opération d'E / S, libérant le thread d'interface utilisateur pour faire d'autres choses, et retournant plus ou moins automatiquement au thread d'interface utilisateur une fois que vous avez fini de lire le fichier - sans jamais avoir à le faire créer un nouveau fil.la source