Utilisation de SynchronizationContext pour renvoyer des événements à l'interface utilisateur pour WinForms ou WPF
Je suis en utilisant un SynchronizationContext de maréchal des événements de nouveau à la thread de l'INTERFACE utilisateur de ma DLL qui fait beaucoup de multi-thread tâches en arrière-plan.
Je sais que le pattern singleton n'est pas un favori, mais je l'utilise pour l'instant pour stocker une référence de l'INTERFACE utilisateur du SynchronizationContext lorsque vous créez foo de l'objet parent.
public class Foo
{
public event EventHandler FooDoDoneEvent;
public void DoFoo()
{
//stuff
OnFooDoDone();
}
private void OnFooDoDone()
{
if (FooDoDoneEvent != null)
{
if (TheUISync.Instance.UISync != SynchronizationContext.Current)
{
TheUISync.Instance.UISync.Post(delegate { OnFooDoDone(); }, null);
}
else
{
FooDoDoneEvent(this, new EventArgs());
}
}
}
}
Cela ne fonctionne pas du tout dans WPF, le TheUISync instances de l'INTERFACE utilisateur de synchronisation (qui se nourrissent à partir de la fenêtre principale) ne correspond jamais avec l'actuel SynchronizationContext.Actuel. Dans un formulaire windows quand je fais la même chose qu'ils vont match après une invoquer et nous reviendrons vers le thread approprié.
Ma dose, je déteste ça, ressemble
public class Foo
{
public event EventHandler FooDoDoneEvent;
public void DoFoo()
{
//stuff
OnFooDoDone(false);
}
private void OnFooDoDone(bool invoked)
{
if (FooDoDoneEvent != null)
{
if ((TheUISync.Instance.UISync != SynchronizationContext.Current) && (!invoked))
{
TheUISync.Instance.UISync.Post(delegate { OnFooDoDone(true); }, null);
}
else
{
FooDoDoneEvent(this, new EventArgs());
}
}
}
}
J'espère donc que cet échantillon fait assez de bon sens à suivre.
source d'informationauteur Joel Barsotti
Vous devez vous connecter pour publier un commentaire.
Le problème immédiat
Votre problème immédiat est que
SynchronizationContext.Current
n'est pas configuré automatiquement pour WPF. De jeu, vous aurez besoin de faire quelque chose comme ceci dans votre TheUISync code lors de l'exécution sous WPF:Un problème plus profond
SynchronizationContext
est lié à la COM+ et est conçu pour traverser les threads. Dans WPF, vous ne pouvez pas avoir un Répartiteur qui s'étend sur plusieurs threads, donc onSynchronizationContext
ne peut pas vraiment la croix-fils. Il y a un certain nombre de scénarios dans lesquels uneSynchronizationContext
pouvez passer à un nouveau thread - en particulier tout ce qui appelleExecutionContext.Run()
. Donc, si vous utilisezSynchronizationContext
de fournir des événements pour WinForms et WPF clients, vous devez être conscient que certains scénarios de rupture, par exemple une requête web à un service web ou un site hébergé dans le même processus serait un problème.Comment se déplacer besoin SynchronizationContext
À cause de cela, je suggère l'utilisation de WPF
Dispatcher
mécanisme exclusivement à cette fin, même avec les WinForms code. Vous avez créé un "TheUISync" classe singleton qui stocke la synchronisation, ce qui indique clairement que vous avez d'une certaine façon à crochet dans le haut niveau de l'application. Cependant vous le faites, vous pouvez ajouter du code qui crée ajoute quelques WPF contenu de votre application WinForms, de sorte queDispatcher
de travail, puis utiliser la nouvelleDispatcher
mécanisme qui je décris ci-dessous.À l'aide de Dispatcher au lieu de SynchronizationContext
WPF
Dispatcher
mécanisme réellement élimine le besoin pour un autreSynchronizationContext
objet. Sauf si vous avez certains de l'interopérabilité des scénarios de partage de code avec des objets COM+ ou WinForms de l'Isu, la meilleure solution est d'utiliserDispatcher
au lieu deSynchronizationContext
.Cela ressemble à:
Remarque que vous n'avez plus besoin d'un TheUISync objet WPF poignées de détails pour vous.
Si vous êtes plus à l'aise avec l'ancienne
delegate
syntaxe, vous pouvez le faire de cette façon au lieu:Aucun bug à corriger
Notez également qu'il existe un bug dans votre code d'origine qui est reproduite ici. Le problème est que FooDoneEvent peut être mis à null entre le moment OnFooDoDone est appelé et le temps de la
BeginInvoke
(ouPost
dans le code d'origine) appelle le délégué. Le fix est un second test à l'intérieur du délégué:Plutôt que de comparer à l'actuel, pourquoi ne pas simplement laisser il s'inquiéter; il est simplement un cas de manipulation du "contexte" de cas: