Laden...

Thread vs. Event

Erstellt von Hajoseb vor 15 Jahren Letzter Beitrag vor 15 Jahren 3.203 Views
Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 15 Jahren
Thread vs. Event

So.

Jetzt hab ich massig über Threads/Threadpool/BackgroundWorker gelesen.

Da stellt sich mir noch eine Frage.

Wenn ich aus einer Berechnungs-Methode in kurzen Abständen per Event eine Nachricht an (m)eine GUI-Methode sende, die diese Ergebnisse dann Anzeigt (z.B. Anzahl der gefundenen Ergebnisse), brauche ich dann eigentlich noch einen Thread bzw. Backgroundworker?

Durch das Event wird doch die Anzeigenänderung "zwischendurch" ausgeführt, oder?

Das müsste doch so laufen, wie mehrere Threads auf einem Einkern-Prozessor ...

  • rechnen

  • event

  • anzeigen

  • weiterrechnen

  • event

  • anzeigen

  • weiterrechnen

  • event

  • anzeigen

  • ...

oder habe ich da einen Denkfehler ?

Soweit ich das verstanden habe, wird doch ein Event quasi zwischendurch abgearbeitet. Wenn ich auf einen Button klicke brechen ja auch nicht alle anderen Methoden einfach ab, oder ???

Mfg Hajoseb

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

Gelöschter Account
vor 15 Jahren

Soweit ich das verstanden habe, wird doch ein Event quasi zwischendurch abgearbeitet. Wenn ich auf einen Button klicke brechen ja auch nicht alle anderen Methoden einfach ab, oder ???

doch das tun sie. ein event auslösen ist das gleiche wie x-methoden aufrufen. wenn du methoden aufrufst, dann werden diese doch auch nciht übersprungen?

ich denke du solltest dich eingehender mit threading auseinandersetzen. noch hast du den dreh nciht raus.

Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 15 Jahren

Wenn dann nur das mit den Events ...

Das heißt, wenn ich ein Event in einer Methode auslöse, ist diese Methode dann zu Ende ??? Ich möchte aber z.B. jedes Ergebnis innerhalb einer Schleife per Event an eine Sammel-Klasse melden ... geht das dann also nicht ???

Ich dachte Events (habe mich noch nicht daran versucht, aber das soll jetzt der nächste Schritt sein) sollen doch "nur" dazu dienen, Meldungen an andere Methoden zu senden (z.B. in der GUI den Klick auf einen Button. Des GUI läuft doch dann auch weiter ...). Das Event ist doch keine art von Return das die Methode beendet.

Kann ich denn so daneben liegen 8o

Mfg Hajoseb

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

Gelöschter Account
vor 15 Jahren

dann mach doch mal zu testzwecken in der klickmethode des buttons ein Thread.Sleep(10000) und versuch nach dem klicken die form zumzubewegen. du wirst feststellen, das sie sich nciht neuzeichnet, da sie blockiert ist.

edit:

Wenn dann nur das mit den Events ...

ich glaube dann lieber beides.

Gelöschter Account
vor 15 Jahren

ok ich habe beschlossen ausführlicher zu werden:

ein event besitzt einen delegate. dieser delegate ist ein multicastdelegate. das bedeutet, das er eine ansamlung an methoden in sich hat (so was ähnliches wie methodenzeiger), die ggf aufgerufen werden. deshalb muss der delegate des events auch die selbe signatur haben, wie die methoden, die sich bei ihm (oder dem event) registrieren möchten. wenn du nun ein event auslöst, wird der multicastdelegate aufgerufen, der dann cnihts anderes macht wie nacheinander die methoden, die sich registriert haben, aufzurufen.

wenn sich nun z.b. 10 methoden mit += registriert haben und jede einzelne benötigt 10 sekunden um fertig zu sein, dann wird das event 10*10 sekunden benötigen um abgearbeitet zu sein.
beispiel:

machewas();
FeuereEvent(); //<- hier wartet er bis alles fertig ist
machenochwas();//wird erst nach allen eventmethoden aufgerufen

was anderes ist, wenn du einen thread erstellst, der das event feuert. aber grundsätzlich gilt: events und alle methoden die sich an ihm registriert haben, werden im selben thread ausgeführt.

deshalb auch das problem, wenn events aus anderen abarbeitenden threads kommen und man die gui aktuallisieren möchte. denn dann benötigt man invoke.

Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 15 Jahren

Lass uns doch mal auf die Events konzentrieren ...

Wenn ich nun eine FOR-Schleife habe ...


for (int X = 0; X <= 10; X ++)
{
  int Z = X;   //rechne ..., rechne ..., rechne ... 

  ErgebnisWeiterleitenEvent(Z);

  Z = 0;
}

bekomme ich keine 10 Events mit den Ergebnissen ?

Dann hab ich ja bei den Events alles falsch verstanden ... X( X( X(

Mfg Hajoseb

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

G
497 Beiträge seit 2006
vor 15 Jahren

Wenn dann nur das mit den Events ...

Das heißt, wenn ich ein Event in einer Methode auslöse, ist diese Methode dann zu Ende ??? Ich möchte aber z.B. jedes Ergebnis innerhalb einer Schleife per Event an eine Sammel-Klasse melden ... geht das dann also nicht ???

nein, natürlich ist die Methode dann nicht beendet. Das Event wird ausgelöst und die Eventhandler werden ausgeführt. Danach kehrt die Ausführung zum nächsten Prozedurschritt nach der Auslösung des Events zurück. Events sind in diesem Zusammenhang nichts anderes als Methodenaufrufe, bei denen die Anzahl und die Namen der Methoden erst in der laufenden Anwendung ermittelt werden (eben durch Abonnieren eines Events).

Ein Event wird immer in dem Thread abgearbeitet, in dem es ausgelöst wird. Wird in dem Event in irgendeiner Form auf eine Benutzerinteraktion gewartet (darf natürlich nur im GUI-Thread passieren!) oder durch lock() oder ähnliches die Ausführung angehalten, steht dieser Thread und wartet darauf, daß die Blockierung aufgehoben wird. Löst der Thread aber ein Event aus, das durch Invoke() z. B. vom Backgroundworker zum GUI-Thread weitergereicht wird, kann das Event den Backgroundworker nicht mehr blockieren - der Worker löst das Event aus, durch Invoke() wird dieser Aufruf aber in einen anderen Thread verschoben und der Backgroundworker kann sofort weiterarbeiten. Das wäre beispielsweise eine praktikable Methode für deine Anforderung: der Backgroundworker rechnet und sendet in bestimmten Abständen Fortschrittsmeldungen über ein Event an den GUI-Thread, der diese Meldungen dann anzeigt. Dabei aber wie gesagt niemals das Invoke vergessen, ansonsten krachts gegebenenfalls bei der Ausführung.

das ganze trifft übrigens auch bei einem Einkern-Prozessor zu - auch wenn dieser keine echte parallele Verarbeitung beherrschen kann, wechselt das Betriebssystem je nach Priorisierung ständig zwischen den laufenden Threads, so daß diese zwar nicht parallel, aber unabhängig voneinander laufen.

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo Hajoseb,

bekomme ich keine 10 Events mit den Ergebnissen ?

nein, du bekommst 11 Events. 🙂

Die laufen aber alle in dem gleichen Thread. Wenn du deine langlaufende Methode im GUI-Thread laufen lässt, wir das GUI blockiert. Dabei spielt es überhaupt keine Rolle, ob die langlaufende Methode Events feuert oder nicht. Das Feuern eines Events ist nichts weiter als der synchrone Aufruf alle EventHandler-Methoden, die für den Event registriert sind. Du bist also völlig auf dem Holzweg.

Siehe auch [FAQ] Warum blockiert mein GUI? und [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke).

herbivore

Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 15 Jahren

nein, natürlich ist die Methode dann nicht beendet. [...]

Na, dass ist ja dann doch (ungefähr) so, wie ich es mir gedacht haben =)

Ich könnte also (in einen Backgroundworker) die Werte berechnen und immer per Event an die GUI oder eine andere Klasse senden ... Richtig ???

Reicht da ein Event oder muss ich
BackgroundWorker..::.ReportProgress-Methode (Int32, Object)
nehmen ???

Mfg Hajoseb

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

G
497 Beiträge seit 2006
vor 15 Jahren

die ReportProgress-Methode löst nur das ProgressChanged-Event des Backgroundworkers aus. Ob du diese Methode benutzt (und im GUI-Thread dieses Event abonnierst) oder ein eigenes Event implementierst, bleibt dir überlassen. ReportProgress kann neben dem Fortschritt in Prozent auch ein beliebiges Objekt für weitere Detailinformationen zum Fortschritt übergeben, prinzipiell muss man dafür nicht noch ein eigenes Event bauen.

Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 15 Jahren

Danke !!!

jetzt weiss ich wenigstens, dass ich auf dem richtigen Weg bin und doch nicht ganz so dämlich, wie ich erst dachte ... 🙂

Jetzt muss ich nur noch überlegen, ob ich (mehrere) BackgroundWorker oder den ThreadPool nehme (wegen dem nutzen mehrerer Cores)

Mfg Hajoseb

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo Hajoseb,

Ich könnte also (in einen Backgroundworker) die Werte berechnen und immer per Event an die GUI oder eine andere Klasse senden ... Richtig ???

da EventHandler in dem Thread laufen, der den Event feuert, musst du Control.Invoke verwenden, wenn du willst, dass der Aktualisierungscode auch wirklich im GUI läuft. Deshalb hatte ich auch oben die Link gepostet.

der muss ich BackgroundWorker.ReportProgress-Methode (Int32, Object) nehmen ???

Das ist sinnvoll, denn ReportProgress sorgt dafür, dass der ProgressChanged-Event automatisch im GUI läuft.

herbivore

Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 15 Jahren

Das ist sinnvoll, denn ReportProgress sorgt dafür, dass der ProgressChanged-Event automatisch im GUI läuft.

Ich könnte also damit KEIN Event an einen anderen Thread senden 🤔

Mfg Hjoseb

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo Hajoseb,

warum solltest du das tun wollen? Davon abgesehen haben wir ja schon geschrieben, dass ein Event immer in dem Thread läuft, der ihn feuert. Und: "Das Feuern eines Events ist nichts weiter als der synchrone Aufruf alle EventHandler-Methoden, die für den Event registriert sind."

Das man einen "Event" bzw. die Ausführung eines EventHandlers an den GUI-Thread "weiterleiten" kann, liegt an der Existenz von Control.Invoke und hat nichts speziell mit Events zu tun.

Siehe auch Eigene Exceptions in einem Thread werden nicht abgefangen

herbivore

0
767 Beiträge seit 2005
vor 15 Jahren

Ich könnte also damit KEIN Event an einen anderen Thread senden 🤔

Vielleicht denkst du wegen irgendwelcher antiker Techniken, dass Events sowas wie Interrupts oder Messages sind...

Vergiss das (egal ob du sie dir so vorstellst oder nicht).

Events sind simpel gesagt eine Sammlung von registrierten Methoden. Das Event auszulösen bedeuted nichts anderes als alle Methoden der Reihe nach aufzurufen. Nachdem alle an dem Event registrierten Methoden ausgeführt wurden macht die Methode die das Event ausgelöst hat weiter.

Die Events laufen als nie in einem anderen Thread, und "Senden" kann man events eigentlich nicht, sondern eben höchstens aufrufen.

Wenn du "einen Event an einen anderen Thread senden" willst, dann musst du in der Methode die das tun soll zuerst in den anderen Thread wechseln und dann auslösen - was eher so passiert, dass dem anderen Thread mitgeteilt wird, dass er das Event auslösen soll... zB:


Control.BeginInvoke(delegate{ OnMyEvent(EventArgs.Empty) });

loop:
btst #6,$bfe001
bne.s loop
rts

Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 15 Jahren

Ich könnte also damit KEIN Event an einen anderen Thread senden 🤔

Vielleicht denkst du wegen irgendwelcher antiker Techniken, dass Events sowas wie Interrupts oder Messages sind...

Vergiss das (egal ob du sie dir so vorstellst oder nicht).

...

Das hatte ich wirklich gedacht. Insbesondere durch
BackgroundWorker.ReportProgress-Methode (Int32, Object)

Also muss ich die Klasse, die die Ergebnisse zur Weiterverarbeitung entgegennehmen sollen mit an die Thread(s) übergeben und dann eine der IHR zugehörigen Methoden aufrufen ?

Gibt es andere Wege, eine/mehrere (Ergenis)-Nachricht(en) an eine Klasse zu senden, wenn Event NICHT der Weg ist ???

Die Methoden in den Threads sollen rechnen und dabei die einzelnen Ergebnisse abliefern, die dann in einer Klasse zur Weiterverarbeitung gesammelt werden, bis alle Ergebnisse da sind ...

... in einer List<t> als Ergebnis ...

Mfg Hajoseb

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

0
767 Beiträge seit 2005
vor 15 Jahren

Die BackgroundWorker.ReportProgress() Methode geht intern sicher den Weg über Control.Invoke() um dann am GuiThread den ProgressChanged Event auszulösen.

Also muss ich die Klasse, die die Ergebnisse zur Weiterverarbeitung entgegennehmen sollen mit an die Thread(s) übergeben und dann eine der IHR zugehörigen Methoden aufrufen ?

Daten an einen Thread übergeben ist kein Problem, wenn er mit den Daten gestartet wird und wenn er fertig ist, diese zurückgeben soll. Dann kannst du die Daten ja als Parameter übergeben (Klasse ThreadStart)

Wenn du eine Art eigenen BackgroundWorker hast, der immer laufen soll und wartet dass er was zu tun bekommt, übergib die daten zB über ein Property des threads. In dem Fall sollte der Thread dann aber nicht dauernd prüfen ob er was zu tun hat, sondern mit der Klasse AutoResetEvent ein Signal zum Arbeiten bekommen.

Ich hoff ich hab jetzt nicht das Thema verfehlt...

loop:
btst #6,$bfe001
bne.s loop
rts

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo Hajoseb,

Die Methoden in den Threads sollen rechnen und dabei die einzelnen Ergebnisse abliefern, die dann in einer Klasse zur Weiterverarbeitung gesammelt werden, bis alle Ergebnisse da sind ...

was du willst, ist der Klassische Fall von [FAQ] Warum blockiert mein GUI? und [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke). Ich hatte das schon oben gepostet. Wäre schön, wenn du dir das jetzt mal gründlich durchlesen könntest, damit wir dieses Thema beenden können.

Das hatte ich wirklich gedacht.

Ich hatte oben schon mehrfach geschrieben, dass es nicht so ist, bzw. wie stattdessen es ist. Du hättest es also besser wissen können und müssen. Bitte lies die Antworten, die dir gegeben werden gründlicher.

herbivore

R
258 Beiträge seit 2007
vor 15 Jahren

Ich könnte also damit KEIN Event an einen anderen Thread senden 🤔

Vielleicht denkst du wegen irgendwelcher antiker Techniken, dass Events sowas wie Interrupts oder Messages sind...

Vergiss das (egal ob du sie dir so vorstellst oder nicht).

...

Ein Interrupt macht ja genau das (zumindest bei Mikrocontrollern): Das Hauptprogramm wird unterbrochen (Hier blockiert jetzt das GUI), alles abgearbeitet, was der Interrupt zu sagen hat (=Event), dann wird wieder der GUI-Thread weiterbearbeitet (GUI wieder verfügbar). Wichtig ist dabei, dass das Programm exakt an die Stelle zurückspringt (goto lässt grüßen), an der es vorher die Bearbeitung stillgelegt hat, um das Event zu bearbeiten.
Dabei hat das Ereignis eine höhere Priorität, wird aber nicht als Thread ausgeführt(Sonst könnte man Events ja systemseitig ignorieren(Prio niedriger als GUI-Methoden)).

mfg, Rasta

Sogar meine Mailadresse ist .NET 🙂

3.971 Beiträge seit 2006
vor 15 Jahren

Vllt. noch ein Tip, wenn du feststellst, dass Events die ganze Prozedur verlangsamen, dann hau die überflüssigen Events raus.

Zum Hintergrund: Es ist zwar richtig, dass du über Events Ereignisse an andere Klassen (in dem Fall Windows.Forms) weitergeben sollst (auch unter dem Namen Observer bekannt), falsch hingegen ist es, wenn du 2 Worker-Threads hast und jeder Thread pro Sekunde etwa 1.000 Berechnungen schafft. Du kannst jetzt aber nicht nach jeder Berechnung ein Event auslösen, da das für die Gui 2.000 Änderungen pro Sekunde macht und das schafft diese nicht, braucht mehr Zeit und hält in der Zeit die beiden Worker-Threads auf, was die Gesamtperformance wieder runter zieht.

Es gibt zwei Möglichkeiten (vllt. auch mehr) die das Problem lösen:* Du löst das Event nur alle 100 Operationen aus. Vorteil, leicht und schnell umzusetzen, lößt dennoch aber das Problem nicht wirklich, zumal wenn du von 2 auf 4 Threads gehts, die Zahl sich wieder erhöht

  • Zweite Variante, du arbeitest mit einer Hilfsklasse, wo die Ergebnisse drin gespeichert werden und ein paar zusätzlichen Information zu dem was der entsprechende Thread gerade tut (Verarbeitet Datei xyz). Die Hilfsklasse gibts für jeden erstellten WorkerThread und die Gui greift mittels einem Timer alle xyz-Millisekunden auf die Hilfsklasse zu und holt sich nur die derzeitigen gültigen Information und schreibt sie in die entsprechende Controls. Wichtig hierbei, hier solltest du Synchronisationsmechanismen wie lock(Monitor), Mutex, Waithandle usw. einsetzen, da du mit mehreren Threads auf gemeinsam genutzte Variablen zugreifst.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

0
767 Beiträge seit 2005
vor 15 Jahren

Ein Interrupt macht ja genau das (zumindest bei Mikrocontrollern): Das Hauptprogramm wird unterbrochen (Hier blockiert jetzt das GUI), alles abgearbeitet, was der Interrupt zu sagen hat (=Event), dann wird wieder der GUI-Thread weiterbearbeitet (GUI wieder verfügbar). Wichtig ist dabei, dass das Programm exakt an die Stelle zurückspringt (goto lässt grüßen), an der es vorher die Bearbeitung stillgelegt hat, um das Event zu bearbeiten.
Dabei hat das Ereignis eine höhere Priorität, wird aber nicht als Thread ausgeführt(Sonst könnte man Events ja systemseitig ignorieren(Prio niedriger als GUI-Methoden)).

mfg, Rasta

WindowsForms ist aber kein MicroController. Und ein Event ist kein Interrupt.
Und nach dem Event gibts auch kein Goto...
Das Ereignis hat auch keine höhere Priorität, es ist einfach nur das nächste was ausgeführt wird... wenn im Programm steht:


Do1(); 
Do2();
Do3();
//...
public void Do2()
{
 Do5();
 Do6();
}

dann werden die ja auch in der Reihenfolge ausgeführt (eh klar). wenn jetzt statt Do2() ein MyEvent(EventArgs.Empty) steht, dann wird dieses dort ausgeführt. Der Unterschied ist nur, dass man beim Event nicht weiss, wieviele Methoden ausgeführt werden, weil sich die Clients dranhängen, und bei der oberen Do2() schon.

loop:
btst #6,$bfe001
bne.s loop
rts

R
258 Beiträge seit 2007
vor 15 Jahren

Klar gibts nen Sprung nach der Ausführung 🙂

Sogar meine Mailadresse ist .NET 🙂

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo Rasta,

aber keinen goto-Sprung, sondern einen return-Sprung.

herbivore

Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 15 Jahren

Danke erstmal.

Ich werde mal versuchen, in den Thread(s) mit "List<Werte>".Add die Werte in meine Sammel-Liste zu sammeln.

ggf. muss ich dass .Add mit einem Monitor absichern.

Da dürfte ich jetzt wohl richtig líegen, oder ?

@herbivore ... Es geht sich nur um eine List<T>, nicht um ein Control.

Mfg Hajoseb

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo Hajoseb,

wenn es nicht ums GUI geht, brauchst du keine Threads. Aber oben hast du geschrieben:

Wenn ich aus einer Berechnungs-Methode in kurzen Abständen per Event eine Nachricht an (m)eine GUI-Methode sende,

Deshalb denke ich schon, dass ich richtig liege.

Wenn du die Add-Methode nur ein einem Thread benutzt und das GUI auf die Liste erst zugreift, wenn sie vollständig ist, brauchst du keinen Monitor.

herbivore

Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 15 Jahren

...

Wenn du die Add-Methode nur ein einem Thread benutzt und das GUI auf die Liste erst zugreift, wenn sie vollständig ist, brauchst du keinen Monitor.

herbivore

Na, dann kommen wir ja zur Lösung 🙂

Geht ein .Add auch mit mehreren Threads ( core-duo ...) ohne Monitor?

Mfg Hajoseb

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo Hajoseb,

ohne Synchronisation geht es dann nicht mehr, wobei es natürlich andere Wege der Synchronisation als nur Monitore gibt.

herbivore

Hajoseb Themenstarter:in
309 Beiträge seit 2007
vor 15 Jahren

Na, dann lese ich mal weiter 😉 ( ... oder gibst du noch einen Tipp 8))

thx Hajoseb

**"Zufall ist das Pseudonym Gottes, wenn er nicht selbst unterschreiben will.” **
Anatole France

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo Hajoseb,

[Artikel] Multi-Threaded Programmierung

herbivore

3.971 Beiträge seit 2006
vor 15 Jahren

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...