J'ai une méthode "partiellement" asynchronique, ce qui signifie qu'un chemin de code exécute l'ASYNC et l'autre fonctionne de manière synchrone. Je ne peux pas actuellement faire la partie synchrone ASYNC, bien que je puisse être capable à l'avenir.
private async void MyDropDownBox_DropDownClosed(object sender, EventArgs e) { //This no longer blocks, but events will not marshal back to UI Thread //causing an exception. await Task.Run(()=> UpdateSomethingAsync()); }
5 Réponses :
Etant donné que à la place mises à jourOrtankaSaSync code> doit accéder au contexte de l'interface utilisateur, il ne doit pas être enveloppé dans une tâche
.Run code> appel. (Vous devriez très rarement, vous devez appeler une méthode
async code> à partir de la tâche code> (code>, généralement si la méthode est implémentée de manière incorrecte et que vous ne pouvez pas le réparer.) p>
dosomethingsynchronique code> devrait être la chose que vous appelez à partir de la tâche
.Run code>. Après tout, le but de cette méthode est d'exécuter de manière asynchrone une méthode synchrone dans un fil de piscine de thread. Donc, utilisez-le uniquement pour la méthode synchrone que vous souhaitez exécuter dans un fil de piscine de fil, pas la méthode asynchrone (soi-disant) qui doit accéder au contexte de l'interface utilisateur. P>
C'est là que j'étais initialement penché, j'essaie juste de m'assurer que je suivrai les meilleures pratiques aussi bien que possible. J'essaie généralement de seulement appeler la tâche.Run au niveau de l'interface utilisateur lorsque l'intention de l'appel est de garder l'UI réactif, mais cela peut être le meilleur compromis dans ce cas.
@CasyWilkins Vous devriez appeler task.run code> lorsque vous avez une méthode synchrone longue en cours d'exécution que vous souhaitez exécuter dans un fil de piscine de fil. Cela ressemble exactement au cas ici.
Je suis avec vous, mais Stephen Cleary semble être contre ce type de chose à moins que la méthode synchrone ne soit liée à la CPU. Mon utilisation initiale de la tâche.Run au niveau de l'interface utilisateur est issue de la question liée ci-dessous, où il discute un scénario quelque peu similaire à la fin de sa réponse. La légère différence dans mon cas est que ce n'est pas un mélange de CPU lié et d'E / S lié, mais un mélange d'async et de synchronisation d'E / S liait, si cela a du sens.
@CaseyWilkins Si votre méthode synchrone bloque simplement sur l'IO Travail, vous le changeriez idéalement pour appeler une version inhérente asynchrone de cette opération IO afin que vous puissiez attendre code>. Mais s'il n'y a pas de version asynchrone intrinsèquement fournie, vous n'avez aucune alternative mais pour faire la chose triste et avoir un fil de piscine de thread restez assis là et attendez-le. Ce n'est pas comme si vous pouviez avoir le fil de l'interface utilisateur assis et attendez-le à la place. Donc, vos seules solutions réelles ici sont complètes la méthode synchrone dans une tâche
.Run code> appelez ou rendez-la fondamentalement asynchrone en n'utilisant pas de synchrones IO nulle part.
Dans votre gestionnaire d'événements, vous pouvez utiliser Je ne sais pas qui continue à le faire, mais Mes commentaires ci-dessous, ce post continue à être supprimé. P> Ceci répond au titre de la question, qui était: p> Comment puis-je remuer un événement de la tâche.Run retour au fil de l'interface utilisateur? P>
blockQuote> Lorsque vous recevez un événement d'un fil différent, il s'agit d'un moyen de mise à jour parfaitement valide de mettre à jour l'interface utilisateur. p> p> invoquer () code> pour mettre à jour l'interface utilisateur comme ceci:
"Lorsque vous recevez un événement d'un fil différent, il s'agit d'un moyen de mise à jour parfaitement valide de mettre à jour l'interface utilisateur." Cet événement est pas i> tir à partir d'un fil non-UI et, en tant que tel, cette citation de la mienne ne dit certainement pas ce que vous prétendez. S'il vous plaît, ne faites pas déformer mes déclarations dans votre réponse.
Hein? ... L'auteur a explicitement déclaré dans leur description originale: "Cependant, les événements soulevés par dosomethingsynchrone font maintenant une exception" donc si dosomethingsynchrone () code> est exécuté via Tâche.Run (), puis le événement serait soulevé par un fil différent. Qu'est-ce que j'oublie ici?
@Servy, où serait l'événement dans mon code d'origine, sinon le fil de la piscine de thread qui est démarré avec thread.Run? Mon hypothèse originale était que les exceptions que je reçois était le résultat de la tentative d'accès de l'interface utilisateur d'un autre fil (piscine de fil) via l'événement.
Oui, Si vous appelez Task.Run CODE> i> Alors ce code que vous passez à ce titre exécutera dans un fil de piscine de fil. Cela est radicalement différent de dire que l'événement est tiré d'un fil non-UI. L'événement incendie dans le fil de l'interface utilisateur. Si vous demandez intentionnellement qu'un code exécuté dans un thread non-UI b>, alors ce code i> sera exécuté dans un fil non-UI. Cela ne signifie pas que l'événement tire d'un fil non-UI.
Je pense que cette solution est valide, bien que peut-être pas aussi élégante. Malheureusement, je ne pense pas que je puisse l'utiliser comme je ne manipulez pas l'événement moi-même, mais plutôt une liaison à la liaison de WinformsSource qui gère un événement de l'état de propriété que je soumets à partir de dosomethingsynchrone.
@CasyWilkins L'événement est tiré du fil de l'interface utilisateur. C'est pourquoi vous pouvez mettre à jour l'interface utilisateur à partir du gestionnaire d'événements tant que vous ne marchez pas explicitement sur un fil de piscine de fil en premier.
@Caseywilkins Vous avez montré les gestionnaires d'événements dans le code dans la question, il semblerait donc que vous sont i> gérer vous-même l'événement.
@Servy, je pense dans ce cas, l'événement tire à partir d'un fil non-UI. La confusion peut provenir de mon formulation de questions. Dans ma question initiale, la méthode des mises à jourOrtOsaSync est appelée via Tâche.Run. Dans les mises à jourOMagants, deux autres méthodes (DosomethingSaSync et dosomethingsync and dosomethingsynchrones) peuvent être appelées, toutes deux peuvent constituer un événement de bien-être, probablement sur le fil de la piscine de thread commencée par la tâche.Run ... qui est là où je suppose que le problème des mensonges.
@Servy L'événement en question est un événement de l'état de bien-être, soulevé par DosomoMoSaSync ou Dosomethingsynchrone. Je ne manipule pas ceux-ci. Les manutentionnaires présents concernent des éléments d'interface utilisateur, peut-être que j'aurais dû les quitter pour plus de clarté.
@CasyWilkins Tous les contrôles WinForms incendient leurs événements dans le fil de l'interface utilisateur. Le gestionnaire d'événements est certainement en cours d'exécution dans le fil de l'interface utilisateur. Vous êtes juste intentionnellement du maréchalant à un fil de piscine de fil et vous avez montré le code qui le fait.
@Caseywilkins Si vous souhaitez manipuler l'interface utilisateur, alors Ne pas explicitement exécuter le code dans un fil de piscine de fil i>. Vous sortez de votre chemin pour quitter le fil de l'interface utilisateur. Si vous voulez mettre à jour l'interface utilisateur, ne faites pas cela. Faites votre travail non-UI dans le fil de piscine THEADS, pas votre ui travail.
@Servy Je ne fais pas référence à des événements de WinForm Commands dans ma question (essayez de ne pas au moins), mais un événement je me tire manuellement, de DosomotroningsaSync ou de dosomethingsynchrone. Il s'agit d'un événement de bien-être qui permet à une liaison à une liaison (sur l'interface utilisateur) savoir qu'une propriété spécifique sur un élément lié a changé et qu'il devrait mettre à jour. L'événement de l'état de propriété est tiré après que DosomotringsaSaSync ou dosomethingsynchrones met à jour certaines propriétés.
@Caseywilkins, il semble donc que votre programme soit conçu de manière à ce que les mises à jour de vos modèles soient prises à partir du thread de l'interface utilisateur (ce qui a du sens, je ne m'attendrais pas à ce qu'ils soient écrits pour se comporter correctement face à des accès multithreads) Donc, les mises à jour de votre modèle sont des mises à jour de l'UI i> du point de vue de votre code et devriez être traitée en conséquence
@Servy, cela dépend de si ASYNC peut être utilisé, ce qui est un cercle complet. J'ai besoin de garder l'UI réactif. Si je peux utiliser ASYNC, génial, je peux essentiellement rester sur le thread de l'interface utilisateur et que toute mise à jour du modèle est effectuée là-bas, ASYNC s'occupe de garder l'interface utilisateur réactive. Si je ne peux pas, je dois alors décharger les mises à jour du modèle sur un fil de piscine de thread via Tâche.Run pour garder l'interface utilisateur réactive. Ces modèles mises à jour finissent par tirer un événement de propriété ... qui peut être sur un thread de piscine de fil si commencé via Tâche.Run. Avoir un sens? Je ne l'explique probablement pas trop bien.
@Caseywilkins Vous devriez utiliser tâche.run code> pour la course longue non-UI i> Opérations. Pas pour les mises à jour de l'interface utilisateur (car alors cela ne fonctionne évidemment pas). Mettez votre code non ui i> dans une tâche i> Task.run code> appelez les résultats dont vous avez besoin, puis mettez à jour l'interface utilisateur avec ces résultats.
Ne mettez pas à jour l'interface utilisateur, ni aucun modèle lié à l'interface utilisateur de l'interface utilisateur de mises à jourOrtanksaSync code> ou l'une des méthodes qu'elle appelle. Créer un
Classe CODE> qui détiendra les données requises pour mettre à jour votre UI et renvoyer une instance de cette classe à partir de
mises à jourSaSaSync code>.
DosomoSaSaSync code> Renvoie une tâche
code> et
dosomethingsynchrone code> vient de renvoyer une instance de
ecclassyouCréré code>. Ensuite, revenir dans
mydropdowbox_dropdowndownosed code> après em> vous
attendre code>
mises à jourSaSync code>, utilisez l'instance renvoyée par
mises à jour "/ code" > Pour mettre à jour votre UI ou votre modèle. P>
public class UpdatedInformation
{
public int UpdateId { get; set; }
public string UpdatedName { get; set; }
public DateTimeOffset Stamp { get; set; }
// etc, etc...
}
public class YourForm : Form
{
private async Task<UpdatedInformation> DoSomethingAsync()
{
var result = new UpdatedInformation();
// Something is awaited...
// Populate the properties of result.
// Do not modify your UI controls. Do not modify the model bound to those controls.
return result;
}
private UpdatedInformation DoSomethingSynchronous()
{
var result UpdatedInformation();
// Populate the properties of result.
// Do not modify your UI controls. Do not modify the model bound to those controls.
return result;
}
private async Task<UpdatedInformation> UpdateSomethingAsync()
{
if (ConditionIsMet)
{
return await DoSomethingAsync();
}
else
{
return await Task.Run(DoSomethingSynchronous);
}
}
private async void MyDropDownBox_DropDownClosed(object sender, EventArgs e)
{
var updatedInformation = await UpdateSomethingAsync();
// Now use updatedInformation to update your UI controls, or the model bound to
// your UI controls.
model.Id = updatedInformation.UpdateId;
// etc...
}
}
Ne disant pas que cela ne fonctionnera pas, mais cela tue tout ma base de données existante, me forçant à mettre à jour manuellement l'interface utilisateur.
sicne vous indique que "[..] donc si vous pouvez le réécrire. < / p> le Vous devez évidemment renommer la méthode et telle, mais cela rendra le code plus maintenu si vous pouvez utiliser un véritable ASYCN IO dans dosomethingsynchronique code> [est] E / S lié" Vous pouvez également le rendre asynchronisé en enveloppant l'opération liée IO dans
Dosomethingsynchrone code> une tâche
.run code>.
dosomethingsynchronique code> est quelque chose comme p>
.configureawait (true) code> pourrait peut-être omettre, mais garantit que le code après l'attente sera programmé dans la synchronisation Orignale, c'est-à-dire l'interface utilisateur. -Thread. P>
Dosomethingsynchrone code> p> p>
figuré je répondrais à cela moi-même après quelques recherches supplémentaires. La plupart des autres réponses sont correctes d'une certaine manière, mais n'expliquent pas nécessairement l'ensemble de l'accord en une fois, je vais donc essayer de résumer ici.
Ce premier extrait de la question fonctionne événement sage, mais bloque si Le chemin synchrone dans les mises à jourOMsaSync est pris. Les événements fonctionnent car "attendre" capture automatiquement la synchronisationContext (ceci est la clé) du fil de l'interface utilisateur, de sorte que tous les événements soulevés à partir de mises à jourAgrandeSync soient repoussés à l'interface utilisateur, via la synchronisationContext. Ceci est juste la manière normale d'utiliser async / attendre: p> tâche.Run fonctionne de la même manière, si vous ne l'utilisez pas pour exécuter un Méthode ASYNC. EM> En d'autres termes, cela fonctionne sans blocage et enverra toujours des événements sur le thread d'interface utilisateur, car les mises à jourommaSync sont remplacées par une méthode synchrone. Ceci est juste l'utilisation normale de la tâche.Run:public async Task UpdateSomethingAsync(){
if (ConditionIsMet){
await DoSomethingAsync;
}else{
await Task.Run(DoSomethingSynchronous);
}
}
Quelle est l'exception et où est-il jeté?
Consultez ma réponse ci-dessous - Appel de fil croix illégal à l'interface utilisateur de DosomoMoSaSaSync ou de dosomethingsynchrone soulevant un événement à l'interface utilisateur.
Un bowvote et un vote pour fermer, sans explication? Wow.