GUI- und WorkerThread

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.

Re: GUI- und WorkerThread

Beitragvon mrz » 20.07.2017, 18:40

Krishty hat geschrieben:Bezog sich auch nicht auf deine Antwort; ich will nur, dass joggel einen Überblick darüber bekommt, was er da tut :)


Also wenn ich mir den Code von BeRsErKeR anschaue gruselt es mir schon:

Der Member "bool shutdown" müsste volatile sein.
Und "listOfLogicActions" ist eine normale List,
also auch kein Garantie betreffend Thread visibility.

Das Beispiel könnte man viel simpler machen indem man einfach eine BlockingCollection<T> verwendet und im Loop Take() aufruft:
https://msdn.microsoft.com/en-us/library/dd381908.aspx

Auszug aus dem Doc:

A call to Take may block until an item is available to be removed or the token is canceled.

Mit sowas wäre es zumindest nicht komplett falsch.
Das ganze Beispiel ist aber vom Lösungsansatz her schon nicht schön,
Thema "besser non-blocking Code schreiben".

Hinweise wie man das sauberer machen kann gabs ja bereits,
fängt z.B. an dass man direkt oder indirekt ein Threadpool verwendet.

Mehr zum Thema:
http://bholley.net/blog/2015/must-be-th ... -code.html
mrz
 
Beiträge: 26
Registriert: 07.08.2008, 14:34

Re: GUI- und WorkerThread

Beitragvon joggel » 20.07.2017, 19:24

Ich werde mir das alles mal morgen genauer anschauen.
Ich danke euch erstmal.
...
Benutzeravatar
joggel
Establishment
 
Beiträge: 1178
Registriert: 06.11.2007, 19:06
Wohnort: Dresden

Re: GUI- und WorkerThread

Beitragvon joggel » 21.07.2017, 07:49

So, habe mir das mal angeschaut.
Der Link von MasterQ sieht mir ziemlich nützlich aus. Danke

Die Variante von Berserker gefällt mir auch gut, aber anscheinend ist das noch keine endgültig "sauber" und sichere Lösung.
Weil ja die Liste nicht Thread-Save ist...oder?


@Thread-Pool
Muß ich mich wohl mal damit beschäftigen. Da ich keine Ahnung habe was genau damit gemeint ist...


@non-blocking Code (mrz)
Wie würde man denn sowas machen? Gibt es dazu Beispiele? Naja...ich kann ja mal Google befragen.


@Krishty
Klingt nach einem Promise oder einem Future. (Ganz ab davon, dass solche Dialoge im selben Thread laufen können, so lange sie modal sind!)

Wie würde man das denn umsetzen was ich vor habe, ohne einen zusätzlichen Thread?


@Allgemein
Was haltet ihr denn eigentlich von der Idee, dass ich GUI und Worker voneinander trennen will?
Also, dass in den GUI Klassen eigentlich nur sowas wie "Actions" aktiviert werden, die aber an einer anderen Stellen erst verarbeitet werden.
Ich möchte quasi die GUI-Klassen nur für die Interaktion benutzen; in ihr soll keine Programmlogik stattfinden...
Meinungen?
...
Benutzeravatar
joggel
Establishment
 
Beiträge: 1178
Registriert: 06.11.2007, 19:06
Wohnort: Dresden

Re: GUI- und WorkerThread

Beitragvon BeRsErKeR » 21.07.2017, 08:50

mrz hat geschrieben:
Krishty hat geschrieben:Bezog sich auch nicht auf deine Antwort; ich will nur, dass joggel einen Überblick darüber bekommt, was er da tut :)


Also wenn ich mir den Code von BeRsErKeR anschaue gruselt es mir schon:

Der Member "bool shutdown" müsste volatile sein.
Und "listOfLogicActions" ist eine normale List,
also auch kein Garantie betreffend Thread visibility.

Das Beispiel könnte man viel simpler machen indem man einfach...


Ich habe lediglich joggels ursprünglichen Code erweitert. Selbst löse ich das auch ganz anders. Im Idealfall trenne ich Konfiguration (UI) und Abarbeitung (Logik) nicht nur durch Threads, sondern auch zeitlich. Soll heißen, ich konfiguriere zuerst und die komplette Logik läuft nach einem Event und die GUI schläft dann so lange. Kommt natürlich immer auf den Anwendungsfall an. Mir ist aber noch nicht wirklich oft etwas untergekommen, wo es sinnvoll war bei aktiver UI größere Logikprozesse im Hintergrund laufen zu lassen. Und wenn doch, bin ich mit einem BackgroundWorker immer ganz gut gefahren, den ich dann entsprechend anlaufen lasse. Parallel da schon pauschal irgendwelche Threads laufen zu lassen, finde ich persönlich nicht wirklich schön. Wenn ich etwas parallel anstoßen will, dann tu ich das doch auch genau an dieser Stelle.


mrz hat geschrieben:Der Member "bool shutdown" müsste volatile sein.


Nö, wieso? Das wird nur vom Main-Thread verändert. Andere Threads greifen da nur lesend drauf zu. Ein gleichzeitiger Zugriff mehrerer Threads ist zudem durch das Wait nicht möglich. Prinzipiell mag das vielleicht korrekt sein, aber das ist eine schnelle Anpassung von joggels Code. Und das passt imho schon so für diesen einfachen Fall.
Ohne Input kein Output.
Benutzeravatar
BeRsErKeR
Establishment
 
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: GUI- und WorkerThread

Beitragvon joggel » 21.07.2017, 09:21

BeRsErKeR hat geschrieben:Selbst löse ich das auch ganz anders. Im Idealfall trenne ich Konfiguration (UI) und Abarbeitung (Logik) nicht nur durch Threads, sondern auch zeitlich. Soll heißen, ich konfiguriere zuerst und die komplette Logik läuft nach einem Event und die GUI schläft dann so lange.

Kann man mal nach etwas Beispiel-Code fragen?
...
Benutzeravatar
joggel
Establishment
 
Beiträge: 1178
Registriert: 06.11.2007, 19:06
Wohnort: Dresden

Re: GUI- und WorkerThread

Beitragvon BeRsErKeR » 21.07.2017, 09:40

Es kommt drauf an, was genau du vorhast. Dein Anliegen war die Logik von der UI zu trennen. Die Frage ist, wie du das genau meinst. Ich nehme an du meinst, dass die Logik nicht im UI-Thread (Main-Thread) läuft, damit die Oberfläche nicht blockiert wird, während die Logik läuft.

Und genau hier stellt sich die Frage, ob es sinnvoll ist, dass der Anwender die UI weiter bedienen kann, während die Logik läuft. Oft ist es ja so, dass der Anwender erst nach der Abarbeitung weitermachen will und kann. In dem Fall, sollte die UI sogar blockiert werden. Zum Beispiel indem man die Controls für die Zeit disabled. Asynchrone Abarbeitung ist ja eher dann sinnvoll, wenn dem Anwender egal ist, wann diese fertig wird und er parallel schon weiterarbeiten kann. Das sieht mir in deinem Beispiel aber nicht so aus.

Die erste Frage sollte also sein, ob hier ein paralleler Thread überhaupt sinnvoll ist. Wenn der Anwender eh auf das Ergebnis der Abarbeitung wartet, dann sehe ich da keinen Sinn drin. Falls die Abarbeitung sehr lange dauert und ggf. vorzeitig abgebrochen können werden soll, dann kannst du über einen BackgroundWorker nachdenken, den du startest, aber dennoch den Großteil der UI disablest.

Beispielcode ist da schwierig ohne dein genaues Ziel zu kennen. Die Anforderung "Logik von UI trennen" ist nicht sehr aussagekräftig und im Kontext von Threading imho auch nicht immer sinnvoll.
Ohne Input kein Output.
Benutzeravatar
BeRsErKeR
Establishment
 
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: GUI- und WorkerThread

Beitragvon joggel » 21.07.2017, 10:23

Naja...ich hielt (und halte das auch noch etwas) für kein sooo gutes Design. Also Logik-Code in UI-Code zu packen...
Mir ging es darum, dass ich die GUI wirklich nur für die Interaktion benutze.
Klar, ich könnte dann in der Funktion, die bei einem Klick auf den Button aufgerufen wird, einfach eine Funktion aufrufen, die das Öffnen + Lesen der Datei übernimmt;
Aber intern würde sich das halt alles innerhalb der onButtonClick-Funktion abspielen....was ich nicht so schön fand.

Mir geht es eher darum, ein "schönes" SW-Design zu konstruieren, in der die GUI eben nur für GUI-Sachen zuständig ist, und alles was nicht GUI ist gehört nicht in den GUI-Code.
...
Benutzeravatar
joggel
Establishment
 
Beiträge: 1178
Registriert: 06.11.2007, 19:06
Wohnort: Dresden

Re: GUI- und WorkerThread

Beitragvon BeRsErKeR » 21.07.2017, 10:47

Naja Code-Trennung hat ja erstmal nichts mit Threads zu tun. Dass du deinen Logik-Code z.B. in eine andere Klasse auslagerst, ist ja erstmal nicht verkehrt.

Wenn du jetzt aber z.B. die Logik in einen Extra-Thread packst und am Ende dann im UI-Thread auf diesen Thread wartest, hast du auch nichts gewonnen. Die Frage ist auch, ob du dir das Leben künstlich schwer machen willst, nur um "gutes Design" zu erreichen.

Wenn es dir so wichtig ist, dann starte doch für jede Logik-Task einen BackgroundWorker und hänge dich an das RunWorkerCompleted-Event, um festzustellen wann er fertig ist. Währenddessen kannst du z.B. Teile des UIs disablen etc. Für mich sieht das aber eher nach "mit Kanonen auf Spatzen schießen" aus.
Ohne Input kein Output.
Benutzeravatar
BeRsErKeR
Establishment
 
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: GUI- und WorkerThread

Beitragvon joggel » 21.07.2017, 11:01

Ja, kann sein das ich da etwas zu viel des Guten will...

Ich werds nochmal überdenken.
Wobei ich den grundgedanken "Logik und UI trennen" ja echt nicht verkehrt finde.
Werd mich da auf jeden Fall noch etwas mit der Thread/Task/etc Geschichten belesen...
...
Benutzeravatar
joggel
Establishment
 
Beiträge: 1178
Registriert: 06.11.2007, 19:06
Wohnort: Dresden

Re: GUI- und WorkerThread

Beitragvon BeRsErKeR » 21.07.2017, 11:11

Wie gesagt der Grundgedanke "Logik und UI trennen" ist schon gut, aber er hat nichts mit Threading zu tun, sondern mit der Trennung von Code.

Eine gute Übung ist hier z.B. die gesamte Logik in einem separaten Assembly (DLL) zu implementieren und dort auf alle UI-spezifischen Referenzen (System.Drawing, System.Windows.Forms, etc) zu verzichten. In deiner UI-Assembly kommunizierst du mit dem Logic-Assembly. Dort sollten dann nur noch direkte UI-Zugriffe (Controls befüllen und lesen) und Schnittstellenaufrufe zur Logik auftauchen.

Ansonsten kannst du dir in diesem Kontext gern auch mal MVC oder MVVM angucken. Wie gesagt sagen diese Modelle etwas zur Code-Trennung aus, aber nicht dazu, dass der Code in verschiedenen Threads laufen muss.
Ohne Input kein Output.
Benutzeravatar
BeRsErKeR
Establishment
 
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: GUI- und WorkerThread

Beitragvon Krishty » 21.07.2017, 11:55

BeRsErKeR hat geschrieben:
mrz hat geschrieben:Der Member "bool shutdown" müsste volatile sein.
Nö, wieso? Das wird nur vom Main-Thread verändert. Andere Threads greifen da nur lesend drauf zu.
Eben – alle lesenden Threads lesen dann den falschen Wert ;)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6040
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: GUI- und WorkerThread

Beitragvon mrz » 21.07.2017, 12:27

Krishty hat geschrieben:
BeRsErKeR hat geschrieben:
mrz hat geschrieben:Der Member "bool shutdown" müsste volatile sein.
Nö, wieso? Das wird nur vom Main-Thread verändert. Andere Threads greifen da nur lesend drauf zu.
Eben – alle lesenden Threads lesen dann den falschen Wert ;)


@BeRsErKeR
Wenn "shutdown" nicht volatile ist dann hast Du keine *Garantie* dass Änderungen für andere Threads sichtbar werden.

Also im MainThread wird irgendwann shutdown auf true gesetzt, für den anderen Thread ist shutdown aber immer noch auf false
und er sieht *evtl* auch nie dass shutdown auf true gesetzt wurde.

Hat mit CPU Caches zu tun.

Natürlich gibt es noch andere Möglichkeiten Sichtbarkeit zu garantieren,
statt volatile könnte man z.B. alle Zugriff auf "shutdown" mit eines Lock machen.

Der Vollständigkeit halber (sonst kommt gleich Krishty ;)) sei noch zu erwähnen dass es, je nach Memory Model zur Runtime,
es spezielle Fälle - oder besser Szenarios - gibt wo *eigentlich* keine Sychronisation nötig ist (Happens-before Regeln).
mrz
 
Beiträge: 26
Registriert: 07.08.2008, 14:34

Re: GUI- und WorkerThread

Beitragvon Krishty » 21.07.2017, 13:12

mrz hat geschrieben:Hat mit CPU Caches zu tun.
Nein, mit dem Compiler. Die Sache läuft so ab:
  1. DoThisAllTheTime() muss optimiert werden.
  2. Es benutzt eine Variable shutdown.
  3. shutdown beginnt false und wird ausschließlich in MainForm_Closed() verändert.
  4. MainForm_Closed() wird in DoThisAllTheTime() niemals aufgerufen.
  5. Also wird shutdown niemals geändert.
  6. Also darf der Optimizer alle Vorkommen von shutdown durch die Konstante false ersetzen.
Resultat: Wenn der eine Thread dann shutdown = true setzt, passiert in dem anderen Thread nichts.

volatile teilt dem Compiler mit, dass sich shutdown eben doch ändern kann, auch wenn es nicht durch den aktuellen Thread geschrieben wird.

Die CPU-Caches spielen da erstmal keine Rolle; tatsächlich bewirkt volatile aber auch, dass der Compiler die Caches synchronisiert, bevor er aus shutdown liest. Auf x86 sind die Caches aber sowieso immer synchron* und da muss nichts gemacht werden.

* Außer bei SSE 2-Befehlen, die die Cache-Hierarchie umgehen; sowas benutzt der C#-Compiler aber nicht.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6040
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: GUI- und WorkerThread

Beitragvon BeRsErKeR » 21.07.2017, 13:36

Hm soweit ich meinte zu wissen, sorgt das WaitAll (genau wie ein lock) dafür, dass die Werte danach aktuell sind, weil es eine memory barrier einführt. An die Compiler-Optimierung habe ich nun nicht wirklich gedacht. Wobei mich diese Optimierung wundern würde, weil selbst für Single-Thread wäre das ja dann ein Problem. Wenn ich eine Variable z.B. in 2 Events benutze, in einem davon setze und in einem anderen auswerte, dann wird das eine Event das andere auch nicht aufrufen - aber eine Optimierung wäre fatal.
Ohne Input kein Output.
Benutzeravatar
BeRsErKeR
Establishment
 
Beiträge: 689
Registriert: 27.04.2002, 22:01

Re: GUI- und WorkerThread

Beitragvon Krishty » 21.07.2017, 14:21

Ich kenne die Event-Semantik in C# nicht, aber normalerweise (Windows-Nachrichten? Dispatch-Interface?) werden die seriell ausgewertet statt parallel. D.h., so lange das eine Event nicht die Message Pump aufruft oder anderweitig die Kontrolle an Runtime/OS abgibt, kann das andere Event unmöglich laufen, und demnach auch nicht die Variable ändern. Damit wäre die Optimierung korrekt.

Es gibt natürlich genug andere Fälle, die explizit nebenläufig sind. Dann braucht man aber entweder Compiler-Unterstützung (ich kann mir gut vorstellen, dass der Compiler Variablen implizit als volatile behandelt, wenn sie von einem Lambda benutzt (captured) werden, das man an was Asynchrones übergibt – dann hat man es ja eh nicht mehr mit „normalen“ Variablen zu tun, auch wenn es der Compiler so aussehen lässt – aber mein C#-Wissen ist hier aber erschöpft) oder eben explizites volatile, wie mrz anmerkte.

Die Moral ist übrigens für joggel, so viele fertige Schnittstellen wie möglich zu nutzen, denn Multithreading selber machen ist – das sehen wir gerade – knifflig …
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
 
Beiträge: 6040
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

VorherigeNächste

Zurück zu Programmiersprachen, Quelltext und Bibliotheken

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 3 Gäste

cron