wie shared man daten zwischen main und subthreads? [gelöst]

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Benutzeravatar
theo
Beiträge: 10927
Registriert: Mo 11. Sep 2006, 19:01

Re: wie shared man korrekt daten zwischen main und subthread

Beitrag von theo »

mal die Frage an Theo, kriegst man/Du das ohne Threads (evtl. mit mehr Daten) auch so hin?
Klar geht das. Siehe Anhang.

Meines Erachtens ist deine Lösung ein Paradebeispiel für Overengineering.
Die Rechnerei für das Array steht im keinem Verhältnis zum Synchronisations- und Zeichenaufwand.

Du könntest sogar for i := 0 to 9999 rechnen, und es würde sich noch nicht lohnen.
Dateianhänge
project1.zip
(3.01 KiB) 148-mal heruntergeladen

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: wie shared man korrekt daten zwischen main und subthread

Beitrag von mschnell »

laz847 hat geschrieben:Ich möchte aber nicht, dass jeder Thread einzeln syncronisiert zeichnet - weil dann alle anderen immer warten müssen bis sie dran sind.
Ist Dir klar, wie "TThread.Synchronize" funktioniert ?
- Der Thread scheduled ein Event für den Mainthred
- Der Thread wartet, bis der Mainthread das Event fertig bearbeitet hat (was beliebig lange dauern kann)
- Andere Threads laufen unbehelligt weiter (außer sie machen ebenfalls "Synchronize).

Vorteil: Einfach zu handhaben.
Nachteil: der Thread bleib beliebig lange hängen.

Alternative: TThread.Queue (oder Appliction.QueueAsyncCall):
- der Thread läuft einfach weiter
- der Mainthread führt die Aktion "irgendwann" aus.

Vorteil: kein Warten für den Thread.
Nachteil: potentielle Daten-Synchronisations-Probleme.

Um Synchronisations-Probleme zu vermeiden, kannst Du dem Mainthread mit dem Event ein Objekt schicken.

- Das Objekt enthält die Daten und das "Event"-Programm, das der Mainthread ausführen soll
- Der Thread macht ein Create für das Objekt und übergibt es mit TThread.Queue (oder Appliction.QueueAsyncCall)
- Des Event-Programmm macht als letzte Anweisung Ende "Free" (auf sich selbst)

-Michael
Zuletzt geändert von mschnell am Mi 1. Apr 2015, 09:55, insgesamt 1-mal geändert.

Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Re: wie shared man korrekt daten zwischen main und subthread

Beitrag von Scotty »

Dynamische arrays sind IMHO schlecht leserlich und außerdem fehleranfällig. Deshalb ein Beispiel mit
a) mtprocs, das dir viel Arbeit beim Multithreading abnimmt
b) einer specialized list, die eine Klasse mit deinen Daten und deren Verrechnung beinhaltet und
c) der Antwort auf deine Frage in diesem Thread: speichere das Ergebnis einfach mit den Daten ab.

Code: Alles auswählen

type 
  TSingleOperation=class
     FData: byte;
  public 
     procedure Run;
  end;
  TProcessingList = specialize TFPGList<TSingleOperation>;
 
  TSingleThread=class(TThread)
    private
      FOperation : TSingleOperation;
    protected
      procedure Execute; override;
    public
      constructor Create;
      destructor Destroy;override;
      property Operation:TSingleOperation read FOperation write FOperation;
    end;
 
procedure TMyWhatEver.DoSomething()
 procedure RunMTProcs(Index: PtrInt; Processing: TProcessingList; Item: TMultiThreadProcItem);
 begin
   TSingleOperation(Processing[Index]).Run;
 end;
begin
  for i:=0 to TotalNumberOfOperations-1 do
  begin
    aSingleOperation:=TSingleOperation.Create;
    aSingleOperation.FData:=random(255);
    //zum Testen einfach single-threaded ausführen
    //aSingleOperation.Run;
  end;
  //multithreaded
  ProcThreadPool.DoParallelLocalProc(@RunMTProcs,0,FProcessing.Count-1,FProcessing);
end;
 
procedure <TThread>.Execute;
  FOperation.Run;
 
procedure TSingleOperation.Run;
  FData:=1;
 
Hier ist ein vollständiger Code: http://sourceforge.net/p/scrabble/code/ ... eforce.pas (mit einer eigenen Lösung für die parallele Ausführung, die technisch schlechter ist, aber dafür ein besseres Feedback ermöglicht)

Alternativ kannst du auch in der Synchronize-Prozedur auf die properties des Threads zugreifen.

BeniBela
Beiträge: 321
Registriert: Sa 21. Mär 2009, 17:31
OS, Lazarus, FPC: Linux (Lazarus SVN, FPC 2.4)
CPU-Target: 64 Bit

Re: wie shared man korrekt daten zwischen main und subthread

Beitrag von BeniBela »

mschnell hat geschrieben:
laz847 hat geschrieben:Ich möchte aber nicht, dass jeder Thread einzeln syncronisiert zeichnet - weil dann alle anderen immer warten müssen bis sie dran sind.
Ist Dir klar, wie "TThread.Synchronize" funktioniert ?
- Der Thread scheduled ein Event für den Mainthred
- Der Thread wartet, bis der Mainthread das Event fertig bearbeitet hat (was beliebig lange dauern kann)
- Andere Threads laufen unbehelligt weiter (außer sie machen ebenfalls "Synchronize).

Vorteil: Einfach zu handhaben.
Nachteil: der Thread bleib beliebig lange hängen.
Noch ein Nachteil:

Läuft nicht unter Android

laz847
Beiträge: 114
Registriert: Mi 18. Jun 2014, 16:39

Re: wie shared man korrekt daten zwischen main und subthread

Beitrag von laz847 »

Klar geht das. Siehe Anhang.
:D läuft gut, aber wie gesagt, das mit den 7 Threads und 7 Tagen und 100 oder 1000 Berechnungen ist hier nur ein Beispiel und wir berechnen lediglich

Code: Alles auswählen

for i := 0 to 99 do FData[i] := ((7.5 - ANr) / 3.5 - 1) / 1.2 + Sin(i / 5 + Cnt * ANr / 50) / 5;
Meines Erachtens ist deine Lösung ein Paradebeispiel für Overengineering. Du könntest sogar for i := 0 to 9999 rechnen, und es würde sich noch nicht lohnen.
Das sind jetzt nicht wirklich aufwendige Berechnungen für welche man Threads benötigt, da habe ich Dir doch bereits zugestimmt? Das hier ist ein fiktives Beispiel, mit meinem Projekt hat dieses Beispiel (bis auf den Ablauf) nichts gemeinsam! Meinst du ich mach solchen Aufwand um 7 Sinus Kurven zu berechnen?

Alleine die ersten Berechnungen erfolgen bei mir über 10 x 500.000 - 1.000.000 Datensätze, die werden umgerechnet, daraus werden Indikatoren berechnet, Live-Optimierungen und das gleichzeitig für 10 verschiedene Inputs usw... Können wir uns jetzt bitte darauf einigen, dass ich es gern mit Threads möchte :D? Je nachdem welche Verzögerung ich einstelle, fahr ich alle 4 Kerne hoch auf 80-90% und wenns schneller geht ist es bei diesem Projekt gut. Ich nutz die auch, versprochen :roll:!
Läuft nicht unter Android
Ich hab überhaupt gar nichts von Android gefragt oder gesagt? Will ich überhaupt nicht nutzen?
Ist Dir klar, wie "TThread.Synchronize" funktioniert ? Der Thread wartet, bis der Mainthread das Event fertig bearbeitet hat (was beliebig lange dauern kann)
Lieber Michael, ich versteh die Frage nicht?
Ich möchte aber nicht, dass jeder Thread einzeln syncronisiert zeichnet - weil dann alle anderen immer warten müssen bis sie dran sind.
Ich glaub ich hab das schon verstanden, deswegen will ich ja Synchronize so selten wie nötig nutzen. Wenn ich es nutze dann syncronisiere ich z.B. Datenübergabe oder Schaltvorgänge, aber ich will eben NICHT, dass ich aufwendige Berechnungen wieder aus dem WorkerThread raus synchronisiere.

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: wie shared man korrekt daten zwischen main und subthread

Beitrag von mse »

Eine andere Möglichkeit ist ein globaler lock im main-thread. MSEgui hat dafür application.lock()/unlock(), man kann das sicher auch in Lazarus einbauen. Das ist zwar bequemer als synchronize(), hat aber den gleichen Nachteil, dass die Rechenthreads auf die Beendigung eines main-loop-Durchgangs warten müssen.
Ich würde die anzuzeigenden Daten in die main-event-queue einspeisen. Falls du für Lazarus etwas entsprechendes nachbauen willst, in MSEgui gibt es die Prozedur tmsecomponent.postcomponentevent():

Code: Alles auswählen

 
procedure tmsecomponent.postcomponentevent(const event: tcomponentevent;
                                  const aoptions: posteventoptionsty = []);
begin
 event.create(event.kind,ievent(self));
 application.postevent(event,aoptions);
end;
 
application.postevent() ist thread sicher. Man erzeugt also im Rechenthread einen tcomponentevent-Nachkommen (tmycomponentevent), packt die berechneten Daten hinein - dazu ist z.B. ein dynamisches Array durch die automatische Speicherverwaltung gut geeignet - und ruft anzeigeformular.postcomponentevent() auf.
Die Daten werden im main-thread vom Anzeigeformular empfangen:

Code: Alles auswählen

 
type
 tdisplayfo = class(tmseform)
[...]
  protected
   procedure receiveevent(const event: tobjectevent); override;
  public
 end;
 
 dataty = record
  a: int32;
  b: int32;
 end;
 dataarty = array of dataty;
 
 tmycomponentevent = class(tcomponentevent)
  public
   fdata: dataarty;
 end;
 
[...]
 
procedure tdisplayfo.receiveevent(const event: tobjectevent);
begin
 if event is tmycomponentevent then begin
  with tmycomponentevent(event) do begin
   //process the data, event will be automatically destroyed by 
   //application main event loop.
  end;
 end
 else begin
  inherited;
 end;
end;
 
Natürlich braucht es noch Massnahmen um zu verhindern, dass mehr Daten geliefert werden als angezeigt werden können, z.B. durch Aufruf von application.checkoverload().

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: wie shared man korrekt daten zwischen main und subthread

Beitrag von mschnell »

BeniBela hat geschrieben: "TThread.Synchronize" ... Läuft nicht unter Android
Echt nicht ?!?!?!?
Das wäre aber ein schwere Bug von Lazarus/fpc.

-Michael

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: wie shared man korrekt daten zwischen main und subthread

Beitrag von mschnell »

laz847 hat geschrieben:Lieber Michael, ich versteh die Frage nicht? ...

... aber ich will eben NICHT, dass ich aufwendige Berechnungen wieder aus dem WorkerThread raus synchronisiere.
Erklär' doch mal, was Du mit "synchronisiere" meinst ?

Willst Du nicht nicht, dass der Thread auf den Mainthrfead wartet (was natürlich völlig katastrophal ist) ? ->
Dann nimm (wie beschrieben) keinesfalls TThread.Synchronize, sondern - nur um Messages an den Mainthread z.B. Aufforderung zum Anzeigen irgendwelcher Statusse zu senden - TThread.Queue oder Application.QueuAsyncCall.

Muss aus Gründen des concurrent Zugriffes auf Daten der Thread warten und Du suchst eine Methode das zu optimieren ? ->
- Die Daten können mit Semaphoren ("CriticalSection") geschützt werden. Der Thread wartet dann nur, wenn es nötig ist.
- Wenn das noch zu viel Wartezeit induziert kann Doppelpufferung helfen: Der Thread bearbeitet ein Stück der Daten in einer Kopie, die ihm alleine gehört (kein Zugriffsschutz notwendig). Die Kopie wird dann (u.U. in einer Critical Section) in den dem Haupt-Datenbestand eingepflegt.

-Michael
Zuletzt geändert von mschnell am Mi 1. Apr 2015, 10:35, insgesamt 3-mal geändert.

Benutzeravatar
theo
Beiträge: 10927
Registriert: Mo 11. Sep 2006, 19:01

Re: wie shared man korrekt daten zwischen main und subthread

Beitrag von theo »

laz847 hat geschrieben: Das sind jetzt nicht wirklich aufwendige Berechnungen für welche man Threads benötigt, da habe ich Dir doch bereits zugestimmt?
...
Können wir uns jetzt bitte darauf einigen, dass ich es gern mit Threads möchte?
Warum wirst du jetzt pampig? Du hast mich gefragt:
Und mal die Frage an Theo, kriegst man/Du das ohne Threads (evtl. mit mehr Daten) auch so hin?
Dann habe ich mir die Mühe gemacht, dir ein Programm zu schreiben, welches genau das zeigt.
Wenn du irgendwas anderes meinst, musst du die Frage anders stellen.

Mir fällt schon länger auf, dass du oft konkrete Fragen stellst und dann allgemeine Antworten erwartest.
Irgendwie läuft das so nicht.

Und außerdem habe ich bei dir den Eindruck, dass du einfach was kompliziertes haben möchtest, aber nicht so ganz den Durchblick hast. In deinem letzten Code, machst du das ganze Thread Brimborium um nach jedem Durchlauf 10ms zu sleepen?
Aber das war jetzt bestimmt auch gar nicht so gemeint sondern nur ein Beispiel... :wink:

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: wie shared man korrekt daten zwischen main und subthread

Beitrag von mschnell »

mse hat geschrieben:Man erzeugt also im Rechenthread einen tcomponentevent-Nachkommen (tmycomponentevent), packt die berechneten Daten hinein - dazu ist z.B. ein dynamisches Array durch die automatische Speicherverwaltung gut geeignet - und ruft anzeigeformular.postcomponentevent() auf.
Das ist exakt das, was ich oben beschrieben habe. Schön, dass MSEGUI da eine Abstraktion für hat. Es geht aber auch sehr einfach mit einem selbst gebauten Objekt, das eine "pseudo - Closure" - Event Funktion (macht "free") hat die dem normalen TThtread.Queue übergeben wird.

Übrigens: "Closures" wurden vor kurzem in der englischen fpc Mailing Liste diskutiert.

-Michael

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: wie shared man korrekt daten zwischen main und subthread

Beitrag von mschnell »

theo hat geschrieben:Mir fällt schon länger auf, dass du oft konkrete Fragen stellst und dann allgemeine Antworten erwartest.
Irgendwie läuft das so nicht.
Da hat er recht. Ich habe auch Probleme den Hintergrund der Fragen zu erahnen.

-Michael

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: wie shared man korrekt daten zwischen main und subthread

Beitrag von mse »

mschnell hat geschrieben:Es geht aber auch sehr einfach mit einem selbst gebauten Objekt, das eine "pseudo - Closure" - Event Funktion (macht "free") hat die dem normalen TThtread.Queue übergeben wird.
Man sollte nur nicht vergessen den Fall zu behandeln wo das Ziel Objekt nicht mehr existiert.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: wie shared man korrekt daten zwischen main und subthread

Beitrag von mschnell »

mse hat geschrieben:
mschnell hat geschrieben:Es geht aber auch sehr einfach mit einem selbst gebauten Objekt, das eine "pseudo - Closure" - Event Funktion (macht "free") hat die dem normalen TThtread.Queue übergeben wird.
Man sollte nur nicht vergessen den Fall zu behandeln wo das Ziel Objekt nicht mehr existiert.
Verstehe ich nicht. Das fragliche Objekt freed sich mit seine "closure" nur selber. Der Thread, der es erzeugt hat, hält keine Referenz darauf und kann es deshalb nicht fälschlicher weise verwenden. Der Mainthread, der es gesendet bekommt, verwendet es ausschließlich durch Aufruf der im Objekt eingebauten Event-Funktion, die ja eben auch die "Closure" ist.

-Michael

mse
Beiträge: 2013
Registriert: Do 16. Okt 2008, 10:22
OS, Lazarus, FPC: Linux,Windows,FreeBSD,(MSEide+MSEgui 4.6,git master FPC 3.0.4,fixes_3_0)
CPU-Target: x86,x64,ARM

Re: wie shared man korrekt daten zwischen main und subthread

Beitrag von mse »

Mit Ziel Objekt meine ich das main-thread Objekt das die Daten erhält.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: wie shared man korrekt daten zwischen main und subthread

Beitrag von mschnell »

mse hat geschrieben:Mit Ziel Objekt meine ich das main-thread Objekt das die Daten erhält.
War da die Rede von ? Das Problem tritt doch bei tcomponentevent auch auf. Oder nicht ?

Idealer weise stecken die Daten komplett im übergebenen Objekt.

-Michael

Antworten