Laden...

Thread-Synchronisierung, Pause, auf Hardware-Event warten...

Erstellt von 7.e.Q vor 11 Jahren Letzter Beitrag vor 11 Jahren 1.019 Views
7.e.Q Themenstarter:in
925 Beiträge seit 2004
vor 11 Jahren
Thread-Synchronisierung, Pause, auf Hardware-Event warten...

Hi Leute,

ich habe hier ein Problem, das mir das Gehirn ein wenig vermust... folgendes:

in einer Anwendung wird ein IronPython-Script ausgeführt, das sequenziell bestimmte Methoden einer C#-Klasse abarbeitet. Diese Methoden laufen im IronPython-Thread synchron, blockieren also die Ausführung des IronPython-Scripts, sodass jede Methode wirklich erst dann aufgerufen wird, wenn die vorherige beendet ist.

Intern laufen diese Methoden hingegen asynchron. Sie führen in einem eigenen Thread ein Kommando auf einer Hardware aus und der Thread wartet dann auf ein von der Hardware ausgelöstes Complete-Ereignis. Dieses Ereignis lässt die Methode dann letztlich auch im IronPython-Script zurückkehren und selbiges die nächste Methode starten. Das funktioniert bis jetzt ganz brauchbar, auch wenn ich da noch auf die echten Thread-Sync Mechanismen verzichte und an deren Stelle quick'n'dirty zyklisch eine volatile bool'sche Variable abfrage. Beziehungsweise derer zwei, weil die Methoden eigentlich immer darauf warten müssen, dass genau zwei Geräte "Complete" zurückmelden.

Darüber hinaus möchte ich nun aber eine Möglichkeit einbauen, die Ausführung der einzelnen Methoden aus einem anderen Thread heraus zu pausieren. Das heißt, es soll nicht mehr nur auf die "Complete"-Ereignisse der Hardware gewartet werden, sondern zusätzlich und nur sofern von außen angefordert noch auf ein "Vorgang-Fortsetzen"-Ereignis.

Wie ist bei so etwas der Königsweg, um das zu erreichen? Welche Mechanismen zur Threadsynchronisierung nutzt man dabei am besten?

Vielen Dank!

Cheers,
Hendrik

//edit: by the way: ich nutze async/await und Task

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo 7.e.Q,

AutoResetEvent, siehe Consolenanwendung mit Timer optimieren [Semaphore/Ampel].

Und deine Busy-Waiting-Schleife kannst du auch gleich dadurch ersetzen.

herbivore

W
872 Beiträge seit 2005
vor 11 Jahren

Du hast ein typisches Producer/Consumer Pattern und da wuerde ich auf eine BlockingCollection zurueckgreifen.
Der Producer koennte zum Beispiel an den Consumer einen Parameter mit der Ergebnis-BlockingCollection mit. Der Consumer schreibt dann sein Ergebnis in die Collection (Add-Methode) und der Producer wertet das Ergebnis aus (Take auf die Collection) und prueft nach jedem empfangenen Ergebnis, dass keine weiteren Ergebnisse ausstehen.

7.e.Q Themenstarter:in
925 Beiträge seit 2004
vor 11 Jahren

Okay, das mit den WaitHandles funktioniert. Ist allerdings gewaltige Hirnvermusung. Parallel in Threads zu denken, macht keinen Spaß. Gibt es da Methoden, die das Mitdenken bei Multithreading-Anwendungen vereinfachen, den Kopfcompiler verbessern?

Alter Falter...

16.783 Beiträge seit 2008
vor 11 Jahren

Wenn Deine Schichten an für sich sauber sind, dann spielt der Hauptthread in Deiner Business-Schicht ja keine Rolle mehr.
Und wenn das alles sauber umgesetzt ist dann sollte das auch keine Hirnvermusung sein.

Ich mach das oft so, dass Instanzen in einem eigenen Thread laufen und darin parallelisiert wird. Klassisches Beispiel ist hier ein Kern von mir, der Ablagen überwacht und synchron hält.
Die Synchronisierung von Dateien läuft parallel in einem Thread. Sollte die GUI geöffnet sein, dann werden zusätzlich Events an den UI-Thread gepusht.

7.e.Q Themenstarter:in
925 Beiträge seit 2004
vor 11 Jahren

Und wenn das alles sauber umgesetzt ist dann sollte das auch keine Hirnvermusung sein.

Und genau das ist das Problem... Die Anwendung ist über Jahre gewachsen, nein, lass es mich "gewuchert" nennen. Da ist leider nichts mehr sauber drin umgesetzt. Aber um von vorn anzufangen und all die Erfahrungen, die ich im Laufe der Zeit gemacht habe, sauber in einer neuen Software zu verpacken, fehlt leider auch die Zeit. ****** Deadline.

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo 7.e.Q,

was für Fälle man bei nebenläufiger Programmierung beachten muss, zeigt das Beispiel mit zwei konkurrierenden Consumern in SyncQueue <T> - Eine praktische Job-Queue (und die von dort verlinkten Beispiele im Programmierspiel). Davor gibt es keinen Schutz. Anderseits ist es einfach eine Sache von Erfahrung und Übung, solche Fälle zu berücksichtigen und zu durchdenken. Ich versetze mich immer in die Rolle des bösen Onkels, der die fiesest mögliche Situation konstruiert, die ungünstigsten Zeitpunkte für Thread-Wechsel wählt, immer die unpassendsten und grenzwertigsten Daten verwendet, die den Programmablauf möglichst stark durcheinander bringen. Ich versuche nicht zu zeigen, dass der Code in normalen Situationen korrekt ist, sondern ich versuche auf Biegen und Brechen in der abwegigsten Situation doch noch einen Fehler zu finden. Wie gesagt, eine Übungssache. Natürlich muss man dazu die Funktionsweise und die Besonderheiten der Synchronisierungsmechanismen genau kennen. Also Wissen und Erfahrung.

Natürlich kann man versuchen, besonders haarige Stellen zu kapseln, wie es die SyncQueue macht, damit der Benutzer der Klasse weniger Probleme hat. Aber das geht nur bis zu einem gewissen Grad. Es gibt keinen Königsweg. Wenn man versucht, die Komplexität des Synchronisation vor dem Benutzer des Konstrukts zu verstecken, tut man ihm keinen Gefallen, wie das async/await-Beispiel in Task, die mit async/await arbeitet, kehrt vorzeitig zurück zeigt.

herbivore