De la tâche.Le rendement réel des usages?
J'ai lu à propos de Task.Yield
, Et en tant que développeur Javascript, je peux dire que c'est du travail est exactement le même que setTimeout(function (){...},0);
en termes de laisser les principales seul thread traiter avec d'autres trucs aka :
"ne prenez pas la puissance , de la libération de temps en temps - de sorte que d'autres pourraient
avoir un aussi..."
En js c'est en travaillant en particulier dans les grandes boucles. ( ne pas faire le navigateur gel...)
Mais j'ai vu cet exemple ici :
public static async Task < int > FindSeriesSum(int i1)
{
int sum = 0;
for (int i = 0; i < i1; i++)
{
sum += i;
if (i % 1000 == 0) ( after a bulk , release power to main thread)
await Task.Yield();
}
return sum;
}
Comme un JS programmeur, je peux comprendre ce qu'ils ont fait ici.
MAIS en tant que programmeur C#, je me demande : pourquoi ne pas ouvrir une tâche pour elle ?
public static async Task < int > FindSeriesSum(int i1)
{
//do something....
return await MyLongCalculationTask();
//do something
}
Question
Avec Js je ne peux pas ouvrir une Tâche (oui je sais que je peux réellement avec des web workers) . Mais avec c# je peux.
Si oui -- pourquoi même pas la peine avec le fait de sortir de temps en temps alors que je peux le sortir à tout ?
Modifier
L'ajout de références :
De ici :
De ici (un autre livre électronique):
- C'est un mauvais exemple, parce que
Task.Yield
de ne pas conserver l'INTERFACE utilisateur réactive. Aussi,async
etawait
ne pas libérer le thread d'INTERFACE utilisateur; si vous avez un long calcul, vous aurez besoin d'utiliserTask.Run
. - En considérant une application multithread (ASP.NET ou similaire) lorsqu'une tâche est en cours d'exécution sur le fil la piscine et le pool de threads est une ressource précieuse: Si une longue course UC tâche appel
await Task.Yield()
chaque seconde alors d'autres tâches en cours d'exécution devraient avoir une chance sans en attente dans la file d'attente pendant une longue période. Ne serait-ce pas un comportement juste? - La préemption fil de commutation intégré dans chaque système d'exploitation moderne combiné avec l'auto-réglage du pool de threads .NET de travail beaucoup mieux qu'un manuel
await Task.Yield()
.
Vous devez vous connecter pour publier un commentaire.
Quand vous voyez:
vous pouvez pensez-y de cette façon:
Tout cela ne permet de s'assurer de la poursuite qui va arriver de façon asynchrone dans l'avenir. Par de manière asynchrone je veux dire que le contrôle de l'exécution sera de retour à l'appelant de la
async
méthode, et la poursuite rappel pas se produire sur la même trame de pile.Exactement quand et sur quel fil il va se passer dépend entièrement de l'appelant fil de synchronisation du contexte.
Pour un thread d'INTERFACE utilisateur, la suite va se passer à l'avenir itération de la boucle de message, géré par
Application.Run
(WinForms) ouDispatcher.Run
(WPF). En interne, il s'agit du Win32PostMessage
API, qui post un message personnalisé pour le thread de l'INTERFACE utilisateur de la file d'attente de messages. Leawait
poursuite de callback sera appelée lorsque ce message est pompée et traitée. Vous êtes complètement hors de contrôle quand c'est exactement ce qui va se passer.En outre, Windows a ses propres priorités pour le pompage de messages: INFO: Fenêtre de Message Priorités. La partie la plus pertinente:
Donc, si vous utilisez
await Task.Yield()
céder à la boucle de message dans une tentative de garder l'INTERFACE utilisateur réactive, vous êtes réellement à risque d'entraver le thread de l'INTERFACE utilisateur de la boucle de message. Certains dans l'attente de la saisie de l'utilisateur messages, ainsi queWM_PAINT
etWM_TIMER
, ont une priorité plus basse que la publication de continuation de message. Ainsi, si vous neawait Task.Yield()
sur une boucle, vous pouvez toujours bloquer l'INTERFACE utilisateur.C'est comment il est différent de celui du JavaScript
setTimer
analogie que vous avez mentionné dans la question. UnsetTimer
callback sera appelé après tous les utilisateurs d'entrée de message ont été traitées par le navigateur du message de la pompe.Donc,
await Task.Yield()
n'est pas bon pour faire du travail de fond sur le thread d'INTERFACE utilisateur. En fait, vous avez très rarement besoin de lancer un processus en arrière-plan sur le thread de l'INTERFACE utilisateur, mais parfois vous faites, par exemple, l'éditeur de la coloration syntaxique, orthographique, etc. Dans ce cas, utilisez le cadre de veille de l'infrastructure.E. g., avec WPF, vous pourriez faire
await Dispatcher.Yield(DispatcherPriority.ApplicationIdle)
:Pour WinForms, vous pouvez utiliser
Application.Idle
événement:Il est recommandé de que vous ne dépassez pas 50 ms pour chaque itération de cette opération en arrière-plan en cours d'exécution sur le thread d'INTERFACE utilisateur.
Pour un non-thread d'INTERFACE utilisateur sans synchronisation contexte,
await Task.Yield()
passe juste à la poursuite d'un hasard de pool de threads. Il n'y a aucune garantie qu'il va être un différents thread le thread en cours, c'est seulement garanti pour être un asynchrone continuation. SiThreadPool
est affamé, il peut planifier la poursuite sur le même thread.Dans ASP.NET, faire
await Task.Yield()
n'a pas de sens, à l'exception de la solution mentionnée dans @StephenCleary réponse. Sinon, il ne feront que nuire à l'application web de la performance avec une redondance par le fil de l'interrupteur.Donc, est
await Task.Yield()
utile? OMI, pas beaucoup. Il peut être utilisé comme un raccourci pour exécuter la poursuite viaSynchronizationContext.Post
ouThreadPool.QueueUserWorkItem
, si vous avez vraiment besoin d'imposer l'asynchronie sur une partie de votre méthode.Concernant les livres que vous avez cité, à mon avis, ces approches à l'aide de
Task.Yield
sont mauvais. J'ai expliqué pourquoi ils ont tort pour un thread de l'INTERFACE utilisateur, ci-dessus. Pour un non-INTERFACE de pool de threads, il n'y a tout simplement pas de "d'autres tâches dans le thread à exécuter", sauf si vous exécutez une tâche personnalisée pompe comme Stephen Toub estAsyncPump
.Mis à jour pour répondre au commentaire:
Comme un simple exemple: WinForms application:
Form_Load
sera de retour à l'appelant (le WinFroms code de la structure qui a tiréLoad
cas), et puis la boîte de message sera affiché de manière asynchrone, sur l'avenir de l'itération de la boucle de message exécuter parApplication.Run()
. La poursuite de callback est mis en file d'attente avecWinFormsSynchronizationContext.Post
, qui, en interne, messages privés Windows message sur le thread de l'INTERFACE utilisateur de la boucle de message. Le rappel sera exécutée lorsque ce message est pompée, toujours sur le même thread.Dans une application console, vous pouvez exécuter un semblable sérialisation boucle avec
AsyncPump
mentionnés ci-dessus.SynchronizationContext
. Avez-vous regardéAsyncPump
? Après tout, asynchronisme n'est pas que le multi-threading. C'est juste quelque chose qui va se réaliser dans l'avenir. Remarque, même lorsque vous traitez avec desThreadPool
sans s.contexte, il y a une chance que la poursuite aura lieu sur le même fil (par exemple, le même pool de thread peut démarrer et servir plus tard à l'achèvement d'un async opération d'e/S, par hasard).protected virtual Task OverridableMethod() { return Task.Task.CompletedTask; }
. Noteasync
n'est pas une partie de la méthode virtuelle de la signature, de sorte que vous pouvez toujours l'utiliser lors de la substitution de cette méthode dans une classe dérivée.J'ai trouvé
Task.Yield
utile dans deux scénarios:Non, ce n'est pas exactement comme l'utilisation de
setTimeout
de revenir à l'INTERFACE utilisateur. En Javascript qui permettrait de toujours laisser de l'INTERFACE utilisateur mise à jour que lesetTimeout
a toujours un minimum de pause de quelques millisecondes, et dans l'attente de l'INTERFACE utilisateur de travailler a la priorité sur les timers, maisawait Task.Yield();
ne pas le faire.Il n'ya aucune garantie que le rendement permettra à tout le travail sera fait dans le thread principal, au contraire, le code qui a appelé le rendement sera souvent la priorité sur l'INTERFACE utilisateur de travail.
Ref: MSDN: la Tâche.Méthode De Rendement
AsParallel
méthode.Tout d'abord permettez-moi de préciser:
Yield
n'est pas exactement la même quesetTimeout(function (){...},0);
. JS est exécuté dans le thread de l'environnement, de sorte que c'est la seule façon de faire d'autres activités pour se produire. Type de le multitâche coopératif. .net est exécuté dans le multitâche préemptif de l'environnement de manière explicite le multithreading.Maintenant de retour à
Thread.Yield
. Comme je l'ai dit .net vit dans préemptif monde, mais il est un peu plus compliqué que cela. C#await/async
créer un intéressant mélange de ceux multitâche en mode gouverné par des machines à états. Donc, si vous omettezYield
à partir de votre code, il va simplement bloquer le thread et c'est tout. Si vous le faites régulièrement une tâche et il suffit d'appeler de démarrage (ou d'un fil), alors il va juste faire plus de choses en parallèle et, plus tard, bloquer le thread appelant lors de la tâche.Le résultat est appelé. Ce qui se passe lorsque vous neawait Task.Yield();
est plus compliqué. Logiquement, il débloque le code appelant (similaire à la JS) et l'exécution continue. Ce qu'elle ne fait - elle en prend un autre thread et poursuivre l'exécution de préemption de l'environnement avec le thread appelant. C'est donc dans le thread appelant jusqu'à la premièreTask.Yield
et puis il est sur son propre. Les appels suivants àTask.Yield
ne semblent pas faire n'importe quoi.Démonstration Simple:
Voici les résultats:
Si vous déplacez
Task.Yield
sur le dessus de la méthode, il sera par async depuis le début et de ne pas bloquer le thread appelant.Conclusion:
Task.Yield
peut rendre possible de mélanger et de synchronisation de code asynchrone. Certains, plus ou moins réalistes scénario: vous avez une forte opération de calcul et de mémoire cache locale et de la tâcheCalcThing
. Cette méthode vous permet de vérifier si l'article est dans le cache, si oui - article de retour, si elle n'est pas làYield
et de procéder à la calculer. Effectivement échantillon de votre livre n'est pas dénué de sens, parce que rien d'utile est atteint il. Leur remarque au sujet de GUI interactivité est tout simplement mauvais et incorrect (thread d'INTERFACE utilisateur sera verrouillé jusqu'à ce premier appel àYield
, vous ne devriez jamais le faire, MSDN est clair (et corriger) sur: "ne comptez pas sur les attendent Tâche.Rendement(); pour garder une INTERFACE utilisateur réactive".setTimeout(...,0 )
Vous êtes en supposant que la fonction longue est celle qui peut s'exécuter sur un thread d'arrière-plan. Si elle n'est pas, par exemple parce qu'il dispose d'une INTERFACE utilisateur de l'interaction, il n'existe aucun moyen pour empêcher le blocage de l'INTERFACE utilisateur tandis qu'il court, de sorte que le temps qu'il exécute doivent être conservés assez courtes pour ne pas causer des problèmes pour les utilisateurs.
Une autre possibilité est que vous avez plus longue fonctions que vous avez threads d'arrière-plan. Dans ce scénario, il peut être mieux (ou pas, ça dépend) pour empêcher quelques-uns de ces fonctions à partir de la prise de tous vos fils.
await
. droit ?LongRunning
sur elle plutôt que deyield
tous sur la place.Je pense que personne a fourni la réponse réelle lors de l'utilisation de la Tâche.Le rendement.
Il est surtout nécessaire si une tâche utilise une boucle sans fin (ou de longues synchrone de l'emploi), et peut potentiellement être un pool de threads thread exclusivement (pas de permettre à d'autres tâches à utiliser ce fil). Cela peut se produire si à l'intérieur de la boucle, le code s'exécute de façon synchrone. le Tâche.Rendement rééchelonne la tâche pour le pool de threads de la file d'attente et les autres tâches qui attendaient le fil peut être exécutée.
L'exemple:
Task.Yield()
de donner à leurs pairs une chance de s'exécuter. Il ya de meilleures options pour simultanées lié au PROCESSEUR de tels scénarios, par exempleParallel
ou TPL Dataflow.ThreadPool
.ThreadPool
victoires, dans la vraie vie, vous ne pouvez simplement pas monopoliser tous ses threads pour microtasks comme ceux, parce que c'est une ressource partagée utilisée par le runtime ailleurs.