Gründe, die Kontrolle.BeginInvoke würde, nicht führen Sie eine Stellvertretung?
Übersicht
Gibt es Erklärungen für die Kontrolle.BeginInvoke() nicht ausführen, ein Delegat, der es übergeben wird?
Code-Beispiel
Haben wir angenommen, das folgende Muster in unserem Winforms-Anwendungen sicher ausführen UI-bezogenen Arbeit auf dem UI-thread:
private Control hiddenControl = new Control();
private void uiMethod()
{
MethodInvoker uiDelegate = new MethodInvoker(delegate()
{
Logging.writeLine("Start of uiDelegate");
//ui releated operations
childDialog = new ChildDialog();
childDialow.show();
Logging.writeLine("End of uiDelegate");
});
if (hiddenControl.InvokeRequired)
{
Logging.writeLine("Start of InvokeRequired block");
hiddenControl.BeginInvoke(uiDelegate);
Logging.writeLine("End of InvokeRequired block");
}
else
{
uiDelegate();
}
}
Hier, wir erstellen ein Steuerelement "hiddenControl" ausdrücklich für den Zweck der Delegierten auf dem UI-Thread. Wir haben nie Aufruf von endInvoke, weil es anscheinend nicht erforderlich für die Steuerung.BeginInvoke und wir müssen nie um einen Wert zurückzugeben, weil unsere Methoden einfach manipulieren UI, sowieso.
Zwar sehr ausführliche, dieses Muster scheint zu sein, ein relativ gut angenommen Lösung. Es gibt jedoch einige Beweise, dass auch dieses Muster kann nicht gut funktionieren in allen Situationen.
Beobachtungen
Ich bin nicht ausschließt, eine Anwendung, die Fehler und die Schuld WinForms. Nachdem alle, wählen Sie die wahrscheinlich nicht kaputt. Allerdings bin ich an einem Verlust zu erklären, warum ein Delegierter kann scheinbar gar nicht laufen.
In unserem Fall, wir beobachten manchmal, dass die "Start uiDelegate" log-Nachricht wird nie ausgeführt, in bestimmten threading-Szenarien, auch wenn die "Starten von InvokeReqiured block" und "End of InvokeRequired block" erfolgreich ausgeführt.
Es ist schon sehr schwer zu replizieren dieses Verhalten, weil unsere Anwendung wird geliefert, wie eine DLL, unseren Kunden führen Sie es in Ihre eigenen Anwendungen. Deshalb können wir nicht garantieren, wie oder in welchem thread diese Methoden aufgerufen werden können.
Wir ausgeschlossen UI-thread blockieren, weil es wird beobachtet, dass die UI nicht sperren. Vermutlich, wenn das UI aktualisiert wird, dann wird die Nachricht, die Pumpe ist funktionsfähig und für das abrufen von Nachrichten aus der message queue und die Ausführung Ihrer Delegierten.
Zusammenfassung
Angesichts dieser Informationen, gibt es alles, was wir versuchen können, um diese Anrufe mehr Kugel-Beweis? Wie bereits erwähnt, haben wir relativ wenig Kontrolle über andere threads in einer Anwendung, und nicht kontrollieren, in welchem Kontext diese Methoden aufgerufen werden.
Was Sie beeinflussen können, wie die Delegierten erfolgreich zu Steuern.BeginInvoke() ausführen, oder nicht?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Laut MSDN
InvokeRequired
zurückkehren könnenfalse
auch in Fällen, in denenInvokeRequired
solltetrue
- nämlich in dem Fall, dass Sie ZugangInvokeRequired
vor derHandle
dieses Steuerelement/Formular (oder ein Elternteil davon) erstellt wurde.Grundsätzlich werden Ihre check ist unvollständig, das führt zu dem Ergebnis, das Sie sehen.
Müssen Sie prüfen,
IsHandleCreated
, wennfalse
dann sind Sie in Schwierigkeiten, weil einInvoke
/BeginInvoke
notwendig wäre, ABER nicht robust, daInvoke
/BeginInvoke
prüfen, welcher thread erstelltHandle
zu tun, Ihre Magie...Nur, wenn
IsHandleCreated
isttrue
Sie handeln, basierend auf dem, wasInvokeRequired
gibt - etwas entlang der Linien von:Somit folgende ist wichtig, um dieses problem zu vermeiden
Immer stellen Sie sicher, dass die
Handle
ist bereits erstellt, BEVOR der erste Zugriff auf einen anderen thread als dem UI-thread.Laut MSDN Sie müssen nur Referenz
control.Handle
im UI-thread, um ihn zu zwingen, erstellt wird - in deinem code dies muss geschehen, BEVOR das erste mal, wenn Sie Zugriff auf das control/Formular von jedem thread, der nicht der UI-thread.Für andere Möglichkeiten siehe die Antwort von @JaredPar .
Es gibt ein paar Gründe, die eine
BeginInvoke
Aufruf kann fehlschlagen.Es klingt wie es ist sehr gut möglich, dass #2 ist, verursacht Ihr Kummer hier. Ich habe dieses problem schon ein paar mal die Entwicklung von winform-Anwendungen. Es verursacht mir genug Kummer, dass ich wechselte von
Control.BeginInvoke
zuSynchronizationContext.Current.Post
. DieSynchronizationContext.Current
Instanz wird live für die Lebensdauer der UI-thread in einer WinForms app und IMHO ist ein wenig zuverlässiger als Berufung aus einem bestimmtenControl
Sind Sie wahrscheinlich eine Ausnahme in Ihrem ersten Anmeldung rufen. Ich würde vorschlagen, einen Blick in die TPL, obwohl Sie (wenn Sie mit .Net 4.0). Aufgaben machen den code sehr viel besser lesbar, und Sie können etwas tun, wie die folgenden
Dann können Sie auch leicht überprüfen, wenn eine der Aufgaben haben Ausnahmen über continuationTask.Ausnahmen.