Ich verwende einen SynchronizationContext, um Ereignisse zurück zum Benutzeroberflächenthread von meiner DLL zu mappen, die viele Multithread-Hintergrundaufgaben ausführt.Verwenden von SynchronizationContext zum Senden von Ereignissen zurück an die Benutzeroberfläche für WinForms oder WPF
Ich weiß, dass das Singleton-Muster kein Favorit ist, aber ich benutze es jetzt, um eine Referenz des UI-SynchronizationContext zu speichern, wenn Sie foo's Elternobjekt erstellen.
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());
}
}
}
}
Dies überhaupt in WPF nicht funktioniert hat, der TheUISync Instanzen UI-Sync (die aus dem Hauptfenster füttern wird) paßt nie den aktuellen SynchronizationContext.Current. In Windows-Form, wenn ich dasselbe mache, werden sie nach einem Aufruf übereinstimmen und wir werden zum richtigen Thread zurückkehren.
Mein fix, was ich hasse, sieht aus wie
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());
}
}
}
}
Ich hoffe also, diese Probe genug Sinn macht, zu folgen.
Unabhängig davon, wann Sie überprüfen, es sei denn, Sie tatsächlich etwas sperren, ich denke, Sie könnten immer dieses Problem haben. Und das ist ein Threading-Problem, das nicht in Ihrer Klasse behoben werden kann, es muss in der Klasse behandelt werden, die Verkabelung in Ihrem Ereignis ist (oder von Ihrem Ereignis abmelden), obwohl ich ein Sperrobjekt bereitstellen müsste Teilnehmer könnte verwenden und ich könnte verwenden, um sicherzustellen, dass die Ereignisse syncrhornized sind. –
Ja und nein. "public event ..." hat Probleme mit sich selbst, wenn Sie davon ausgehen, dass es sich um "free-threaded" handelt. Wenn zwei Threads beide dasselbe Ereignis zur gleichen Zeit abonnieren, können sie möglicherweise nicht beide in die Aufrufliste gelangen. Daher ist die Annahme immer, dass Sie ein Threading-Protokoll im Hinterkopf haben, um das Ereignis an erster Stelle zu setzen. Das gebräuchlichste Protokoll besteht darin, solche Ereignisse nur für den Benutzeroberflächenthread festzulegen. In diesem Fall funktioniert die Fehlerbehebung, die ich angegeben habe, zuverlässig, während der ursprüngliche Code dies nicht tut. Wenn andererseits ein anderes Protokoll beabsichtigt ist, müssen Sie möglicherweise explizit gesperrt werden. –
Um sicherzustellen, dass 'FooDoDoneEvent' nicht NULL ist, glaube ich, dass Sie wirklich' FooDoDoneEvent' einer lokalen Variablen zuweisen möchten, überprüfen Sie, dass es nicht null ist, und rufen Sie 'FooDoDoneEvent (this, new EventArgs()) auf. '. – Charles