J'ai lu (et utilisé) async / attendre beaucoup de temps pour un certain temps, mais j'ai toujours une question que je ne peux pas avoir une réponse. Dis que j'ai ce code.
private async void workAsyncBtn_Click(object sender, EventArgs e) { await DoAsync(); } private async Task DoAsync() { await Task.Delay(200); statusTextBox.Text += "Call to form"; await Task.Delay(200); }
3 Réponses :
Comme je le comprends, je ne quitte jamais le fil de l'interface utilisateur dans ce cas, toujours il est dirigé de manière asynchrone, l'interface utilisateur est toujours réactive et je peux commencer Plusieurs tâches en même temps et leur application accélère. Comment ce travail peut-il utiliser un seul thread? P> blockQuote>
Premièrement, je vous recommanderais de lire Stephan Clearys Blog Post - Il n'y a pas de fil . P>
Afin de comprendre comment il est possible d'exécuter de multiples unités de travail, nous devons saisir un fait important: Async io Strong> Opérations liées a ( presque em>) rien à faire avec des threads. p>
Comment est-ce possible? Eh bien, si nous perturons au fond du système d'exploitation, nous verrons que les appels aux conducteurs périphérique em> - ceux qui sont chargés de faire des opérations telles que les appels réseau et l'écriture sur disque , tous étaient tous mises en œuvre comme naturellement asynchrones, ils n'occupent pas un fil tout en faisant leur travail. De cette façon, pendant que le pilote de périphérique fait sa chose, il n'est pas nécessaire de ne pas être un fil. Une fois que le pilote de périphérique a terminé son exécution, il signalera le système d'exploitation qu'il est fait via un port d'achèvement des IOCP (I / O), qui exécutera ensuite le reste de l'appel de la méthode (ceci est effectué dans .NET via la threadpool, qui a dédié des threads iocp). P>
Stephans Blog Post le démontre bien: p>
p>
Une fois que le système d'exploitation exécute la DPC (appel de procédure différée) et la file d'attente du paquet de demande d'E / S), il est essentiellement effectué jusqu'à ce que le pilote de périphérique le signale avec le j'ai fini em > Les messages, qui provoquent une chaîne d'opérations entière (décrit dans le blog post) à exécuter, ce qui finira par finir par appeler votre code. P>
Une autre chose à noter est que .NET fait quelque chose "magique" pour nous dans les coulisses lorsque vous utilisez
async-attendre code> motif. Il y a une chose appelée "contexte de synchronisation" (vous pouvez trouver une explication assez longue ici ). Ce contexte de synchronisation est ce qui est en charge d'appeler la suite (code après le premier
attendre code>) sur le fil de l'interface utilisateur à nouveau (dans des endroits où ce contexte existe). p>
EDIT: strong> p> Il convient de noter que la magie avec le contexte de synchronisation se produit également pour les opérations liées à la CPU (et réellement pour tout objet en attente), alors lorsque vous utilisez un thread threadpool via la tâche
de la tâche code> ou < Code> Task.Factory.StartNouveau code>, cela fonctionnera également. P>
Je ne pense pas que l'OP est posé sur les E / S non bloquant dans la méthode ASYNC, je pense qu'il demande au fait que sans utiliser configureawait (faux) code> il peut avoir efficacement plusieurs tâches simultanées. Accéder au contexte actuel de l'UI (c'est-à-dire à l'aide du fil d'interface utilisateur actuel). La réponse est probablement sur les lignes que les tâches simultanées ne sont pas nécessairement exécutées sur plusieurs threads différents.
@Andreas J'aurais peut-être mal compris la question. Peut-être peut-il clarifier?
Je pense que je l'obtiens maintenant. Si nous avions fait WritetoDisk de la manière synchronaise, nous aurions commencé l'opération et attendrions patiemment que cela revienne. Lorsque vous faites des choses asynchrones, nous commençons l'opération (quel que soit le type qu'il est) s'éloigner et laisser l'opération (qui n'a souvent pas besoin d'un fil) dites-nous quand c'est fait. Est-ce correctement (encore simplifié) correct?
@Andreas Oui, c'est une version très simplifiée de la façon dont ASYNC IO fonctionne. Bien que si vous utilisez un fil dans les coulisses (par exemple, lorsque vous appelez avec la tâche code>), vous consommez effectivement un thread threadpool, qui, comme avec ASYNC IO, le maréchal aura sa continuation au maréchal. Contexte de synchronisation de droite si vous êtes
attendre code>.
Vraiment bon exemple d'explication et des liens vers des articles en profondeur. Merci!
@Yuvalitzchakov Quoi de propos des tâches qui fonctionnent sur un thread threadpool Comment sont-ils repoussés sur le fil de l'interface utilisateur, permettez-moi de préciser: dans le cas d'opérations liées à IO, une interruption commence un processus de mise en file d'attente du fil-fil. Quelles files d'attente un rappel d'un fil de fond?
@eranotzap même que pour les opérations liées ASYNC IO. Le contexte de synchronisation les remercie via synchronisationContext.post code>
@Yuvalitzchakov Je continue à gagner la phrase Event / Message Boop. Par exemple, de la façon dont JavaScript fonctionne de manière asynchrone. Le CLR a-t-il un équivalent de cela? Une sorte de mécanisme qui fonctionne et tire l'état de la tâche des tâches de fonctionnement étant attendue ..
Une boucle de message est essentiellement une boucle sans fin qui écoute les événements et les exécute de manière synchrone. C'est ainsi que fonctionnent les applications basées sur l'interface utilisateur. Voir Cette question pour plus de détails. Le modèle basé sur la TPL n'a pas une notion de boucle d'événement, cela fonctionne via un motif d'état-machine.
@Yuvalitzchakov: Techniquement, le DPC fait partie du mécanisme "réponse". Le système d'exploitation envoie l'IRP au pilote, qui indique ensuite à l'appareil de faire le travail, puis de retourner simplement (après avoir marqué le IRP comme "en attente").
Le TaskParallellibrary (TPL) utilise un TaskScheduler Code>, qui peut être configuré avec
TaskScheduler.fromcurrentSynchronizationContext code> Pour revenir à la synchronisationContext comme ceci:
async Task MyMethodAsync()
{
textBox1.Text = "Start";
// The SynchronizationContext is captured here
await Task.Run(() => { DoSomeAsyncWork(); }); // run on the threadPool
// Back on the SynchronizationContext it came from
textBox1.Text = "End";
}
Hmm. Mayby, je ne comprends pas mais dans Dosomeasyncwork, je peux toujours appeler un élément d'interface utilisateur (par exemple dans une application de formulaire) et définir des valeurs. Comment cela peut-il fonctionner si le code est reproché au contexte d'abord sur attendre?
Vous ne devriez pas être capable de faire ça
Donc, après l'attente, vous retournez à la synchronisationContext, il est venu de
Donc, si l'attente est une opération non I / S ou réseau, nous utilisons un autre fil?
Task.Delay n'utilise pas de fil sous-jacent pour reporter une nouvelle exécution. en utilisant la tâche.run (() => {}); utilise-t-il un fil.
Lorsque les appels de fil d'interface utilisateur vous attendent, il démarre l'opération ASYNC et retourne immédiatement. Lorsque l'opération ASYNC se termine, elle notifie un thread de la piscine de thread, mais la mise en oeuvre interne de l'ASYNC attend l'exécution sur le thread d'interface utilisateur qui poursuivra l'exécution du code après l'attente. P>
L'expédition est implémentée au moyen de synchronisationContext qui appelle activement System.Windows.Forms.control.begininvoke. P>
CLR via C # (4ème édition) (référence du développeur) 4ème édition de Jeffrey Richter Page 749 P>
En fait, Jeffrey a travaillé avec SM pour mettre en œuvre l'Async / attendre inspiré par son asyncénumérateur P>
Vous parlez spécifiquement de le maréchalage du travail sur le fil de l'interface utilisateur?
Je vois maintenant que la question n'est pas si précise que je pensais que cela puisse être interprété de différentes manières. Je me suis mieux entendu lire votre réponse @yuvalitzchakov et après avoir lu la réponse de SievaJet, je vois qu'il y a une autre réponse lorsqu'il n'y a pas d'opération liée à l'ASYNC IO.
@Andreas Je vois que vous avez ajouté un autre exemple et cela fonctionne car encore: il n'y a pas de fil impliqué. Avez-vous lu dans blog.stephancleary.com/2013/11/ Il y a-no-thread.html , comme suggéré par Yuval?
@Krumelur ouais je l'ai fait, et cela a un sens parfait maintenant lorsque vous l'avez dirigé. Beaucoup de lecture en ce moment. Le cerveau est partout. Merci!
Pour en savoir plus sur
async-attendre code>, lisez les articles sur Mon
ASYNC-Await Code> Curation
.