Autor Beitrag
GuaAck
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 376
Erhaltene Danke: 32

Windows 8.1
Delphi 10.4 Comm. Edition
BeitragVerfasst: Do 30.05.13 22:57 
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 600
Erhaltene Danke: 23


Delphi 7 PE
BeitragVerfasst: Do 30.05.13 23:15 
Schreiben:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
Aktuell:=aktuell.next;
Aktuell := tdaten.create;
Aktuell.fertig :=false;
//schreibe Daten
Aktuell.fertig :=true;


Lesen (dein Main-Thread)
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
If assigned(erstesElement) then
 If erstesElement.fertig then
  begin
  //Gib Daten aus
  E := ErstesElement;
  ErstesElement := erstesElement.next;
  E.free;
  end;



Das ganze basiert auf einer verketteten Liste die beiden threads bekannt ist..
ausblenden 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;

//create des neuen Threads;
ErstesElement:= TDaten.create;
aktuell := ErstesElement;
Aktuell.fertig :=false;
//schreibe 1 Datensatz
Aktuell.fertig :=true;
//ab jetzt normal weiterschreiben

_________________
Sucht "neueres" Delphi :D
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
Blup
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 173
Erhaltene Danke: 43



BeitragVerfasst: 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.
ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 376
Erhaltene Danke: 32

Windows 8.1
Delphi 10.4 Comm. Edition
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 600
Erhaltene Danke: 23


Delphi 7 PE
BeitragVerfasst: 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 ;)
ausblenden 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;

//implementation

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 ;)

_________________
Sucht "neueres" Delphi :D
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!


Zuletzt bearbeitet von IhopeonlyReader am Di 04.06.13 13:12, insgesamt 1-mal bearbeitet
IhopeonlyReader
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 600
Erhaltene Danke: 23


Delphi 7 PE
BeitragVerfasst: Mo 03.06.13 22:16 
Oder, falls du das meinst:
Beide Threads fürhren DIESELBE procedure aus...
und Beispiel
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
 procedure bsp;
begin
befehl1;
befehl2;
//threadwechsel
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)
ausblenden 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
//nennen wir diese variable (in beiden Units): ww vom Typ Byte; sie muss im initialization initialisiert werden ! (var-unit-beispiel unten)
if ww<=1 then  // < ist nur zur Sicherung, falls mit 0 initialisert wurde
 begin
 //befehl1
 inc(ww);
  end;
if ww<=2 then
 begin
 //befehl2
 inc(ww);
  end;
if ww<=3 then //mal eim Beispiel einer Schleife ! (schleifenvariable MUSS für beide Units zugänglich sein, For-Schleife nicht möglich)
 begin //nennen wir diese Schleifenvariable SW;
 while sw<=100 do //for sw:=1 to 100 do {1 kommt von der Initialisierung}
  begin
  //befehl 3 in schleife
  inc(sw);
  end;
 //initialisiere sw für nächsten durchgang
 sw := 1;
 inc(ww);
  end;  
ww := 1//initialisieren
end;

var-Unit-Beispiel:
ausblenden 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//schleife beginnt bei 1

//finalization
end.

_________________
Sucht "neueres" Delphi :D
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
Blup
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 173
Erhaltene Danke: 43



BeitragVerfasst: Mi 05.06.13 17:41 
user profile iconGuaAck hat folgendes geschrieben Zum zitierten Posting springen:

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:
ausblenden 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: {...usw...}
  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.