Читать книгу Dojos für Entwickler 2 - Stefan Lieser - Страница 53
Listing 5 InvokeRequired nutzen.
Оглавлениеif (listBox1.InvokeRequired) { listBox1.Invoke(new MethodInvoker(() => listBox1.Items.Add(t))); } else { listBox1.Items.Add(t); }
Bevor in diesem Beispiel ein weiterer Eintrag in die ListBox eingefügt wird, wird durch Abfrage von InvokeRequired geprüft, ob der Aufruf mit Invoke erfolgen muss. Ist das der Fall, wird dem Invoke-Aufruf eine Lambda-Expression übergeben, welche den Eintrag in die ListBox schreibt. Invoke sorgt dafür, dass die Lambda-Expression auf dem UI-Thread ausgeführt wird.
Das kann man so machen, doch man sollte es nicht so machen. Denn auf diese Weise wird der UI-Code mit Aspekten der Parallelisierung kontaminiert. Außerdem müsste ein UI, das zunächst synchron und erst später asynchron verwendet wird, modifiziert werden. Das ist unschön. Zum einen verstößt diese Vorgehensweise gegen das Open Closed Principle (OCP), welches besagt, dass eine Klasse offen für Erweiterungen, aber geschlossen gegenüber Modifikationen sein sollte. Noch schwerer wiegt das Argument, dass auf diese Weise das Prinzip die Separation of Concerns (SoC) verletzt würde. Der Aspekt der Parallelisierung würde mit der eigentlichen Aufgabe des UIs vermischt.
Abhilfe schafft die Verwendung des SynchronizationContext bei Windows Forms bzw. des Dispatchers bei WPF und Silverlight. Beides sind Infrastrukturelemente, mit denen die Synchronisation auf einen Zielthread erfolgen kann. Nutzt man diese Infrastruktur, kann man auf einfache Weise einen Synchronizer-Baustein realisieren, der den Result-Event auf dem gewünschten Zielthread ausführt.
Der Synchronizer erhält dieselbe Schnittstelle wie der Asynchronizer: eine Process- Methode als Eingang sowie einen Result- Event als Ausgang. Auch hier sorgt der Baustein wieder dafür, dass der Parameter durchgereicht wird, der Datenfluss also einfach hindurchfließt. Damit der Baustein im Sinne eines Standardbausteins vielseitig einsetzbar ist, erhält er einen generischen Typparameter, der den Datentyp von Ein- und Ausgang bestimmt. Hier zahlt es sich aus, dass beim Flow-Design immer nur maximal ein Parameter verwendet wird. Auf diese Weise ist es nämlich leicht möglich, Standardbausteine zu erstellen und Flows neu zusammenzustöpseln. Zwar könnte man auch Varianten der Standardbausteine anlegen, die mehrere Parameter unterstützen, aber dadurch würde die ganze Sache doch etwas komplizierter.
Listing 6 zeigt die Implementation des Synchronizers für Windows Forms.