Entwickler-Ecke
Windows API - Problem mit Postmessage
GuaAck - Do 30.05.13 22:57
Titel: Problem mit Postmessage
Hallo Experten,
ich habe einen Hauptprozess (MAIN) und einen von diesem erzeugten THREAD. Im THREAD erzeuge ich Daten, die ich im MAIN darstellen will. Ich will keine Thread-Wechsel erzwingen, Windows soll einfach ganz normal den Threads Zeit zuweisen.
Für den Datenaustausch habe ich einen Puffer eingerichtet. Im THREAD habe ich:
FOR alle Daten DO
BEGIN
Schreibe_Datensatz_in_Puffer;
IF Puffer_war_vorher_leer THEN
postmessage(an Main...);
END;
Es gibt eine Bremse für den Fall, dass der Puffer voll ist (die auch gut funktioniert.)
Mein Problem:
a) Gelegentlich: Es läuft so, wie ich möchte: Der Puffer wird gefüllt, Windows schaltet um, MAIN liest den gefüllten Pufferbereich.
b) Meistens leider: Nach jedem postmessage erfolgt ein Threadwechsel und MAIN liest genau einen Datensatz.
Besonders merkwürdig: Wenn ich das Programm wiederholt starte, dann ist es mal so und mal so. Es wird während eines Programmlaufes entweder immer der Puffer genutzt (a)) oder immer jeder Datensatz einzeln per postmessage signalisiert (b)).
Für mich sieht das so aus, als ob statt postmessage meistens intern sendmessage aufgerufen wird.
Hat da jemand eine ähnliche Erfahrung? (Ich nutze Windows XP SP3, Delphi 7.0.)
Gruß GuaAck
IhopeonlyReader - Do 30.05.13 23:15
Schreiben:
Delphi-Quelltext
1: 2: 3: 4: 5:
| Aktuell:=aktuell.next; Aktuell := tdaten.create; Aktuell.fertig :=false; Aktuell.fertig :=true; |
Lesen (dein Main-Thread)
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| If assigned(erstesElement) then If erstesElement.fertig then begin E := ErstesElement; ErstesElement := erstesElement.next; E.free; end; |
Das ganze basiert auf einer verketteten Liste die beiden threads bekannt ist..
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| type TDaten = class public next: TDaten; fertig: Boolean; end;
var ErstesElement: TDaten;
ErstesElement:= TDaten.create; aktuell := ErstesElement; Aktuell.fertig :=false; Aktuell.fertig :=true; |
Blup - Fr 31.05.13 15:17
Moderne Prozessoren haben mehrere Kerne, in der Regel sind die nicht alle ausgelastet.
Wenn also die Nachricht per PostMessage kommt, ist meistens sofort ein Kern für den Hauptthread frei, um diese zu verarbeiten.
Vieleicht werden diese Daten schneller verarbeitet, als neue hinzugefügt werden.
Wenn die Daten möglichst alle zusammen verarbeitet werden sollen, besser erst eine Nachricht senden, wenn alle Daten im Buffer angekommen sind.
Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| criticalsection.Enter; try FOR alle Daten DO BEGIN Schreibe_Datensatz_in_Puffer; END; finally criticalsection.Leave; end postmessage(an Main...); |
Eine Nachricht mehr schadet an dieser Stelle in der Regel nicht. Wurden die Daten bereits mit der letzten Nachricht verarbeitet, passiert halt nichts.
GuaAck - Mo 03.06.13 21:36
Danke Blup,
Criticalsection bezieht sich ja darauf, das der Code nur von maximal einem Thread ausgeführt werden darf. Ich ahbe da keine Erfahrung, habe es aber so in der Hilfe gefunden.
Da habe ich mein Frage etwas ungenau formuliert: "wiederholt starten" in meiner Frage meinte "Prozessstarten-laufen-Prozessende", "Prozessstarten-Laufen..." usw. als einen Lauf nach dem anderen, es gibt also keinen Code, der von mehreren Threads genutzt wird.
Werde mal versuchen, ob eine gegenseitige Verriegelung über Mutex oder Semaphore hilft.
Gruß
GuaAck
IhopeonlyReader - Mo 03.06.13 21:54
Du kannst auch EINE unit schreiben und dann den Thread erstellen (pausiert), die procedure setzten (per setprocedure) und dann kann es gestartet werden ;)
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| type BeispielThread = class(TThread) private DieProcedure: procedure; protected procedure Execute; override; procedure SetProcedure( eineProzedure: procedure ); end;
procedure BeispielThread.SetProcedure( eineProzedure: procedure ); begin DieProcedure := eineProzedure; end; |
So kannst du in 2 verschiedenen Threads "das gleiche" behandeln
Ich hoffe, dass ist das was du wissen wolltest, bzw. es hilft dir ;)
IhopeonlyReader - Mo 03.06.13 22:16
Oder, falls du das meinst:
Beide Threads fürhren DIESELBE procedure aus...
und Beispiel
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| procedure bsp; begin befehl1; befehl2; befehl3; end; |
dass thread 2 thread 1 weiterführt?
wenn, dann wäre das ebenfalls möglich ! Jedoch, naja sehr "umständlich" und verlangsamed,
so würde es gehen:
mindestens 1 Variable (+1 für jede schleife, +1 pro verschachtelte schleife in der schleife) für BEIDE Units zugänglich machen (z.B: extra unit erstellen mit variablen und diese unit in beide thread-unit einbinden)
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
| procedure bsp; begin if ww<=1 then begin inc(ww); end; if ww<=2 then begin inc(ww); end; if ww<=3 then begin while sw<=100 do begin inc(sw); end; sw := 1; inc(ww); end; ww := 1; end; |
var-Unit-Beispiel:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| unit uTVar;
interface
var ww, sw: Byte;
implementation
initialization ww := 1; sw := 1; end. |
Blup - Mi 05.06.13 17:41
GuaAck hat folgendes geschrieben : |
Criticalsection bezieht sich ja darauf, das der Code nur von maximal einem Thread ausgeführt werden darf. ...
|
Eine Critcalsection setzt man häufig dafür ein, wenn auf Daten nicht durch mehrere Threads gleichzeitig zugegriffen werden darf.
Wärend dein Thread also neue Daten in den Puffer schreibt, muss der Hauptthread warten, bis der andere fertig ist.
Ebenso muss der Thread vor dem Hinzufügen neuer Daten warten, wenn der Hauptthread gerade die Daten aus dem Puffer abholt.
Z.B. muss eine Liste geschützt werden, damit nicht gleichzeitig Elemente hinzugefügt und entfernt werden:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30:
| type TMyDatenObject = class() Variable1: end; procedure TMyThread.AddItemToPuffer(AItem: TMyDatenObject); begin criticalsection.Enter; try FList.Add(AItem); finally criticalsection.Leave; end; end;
procedure TMyThread.GetItemFromPuffer: TMyDatenObject; begin criticalsection.Enter; try if FList.Count = 0 then Result := nil else begin Result := FList[0]; FList.Delete(0); end; finally criticalsection.Leave; end; end; |
Falls ein Thread sich gerade in einem Codeabschnitt zwischen Enter und Leave befindet und ein anderer Thread ebenfalls in einen Abschnitt der selben "criticalsection" eintreten will, muss er warten, auch wenn es sich um unterschiedliche Codeabschnitte handelt.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!