Thread ? warum wird zweimal eine Procedure aufgerufen ?

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Thread ? warum wird zweimal eine Procedure aufgerufen ?

Beitrag von pluto »

Hallo,
ich habe ein Seltsammens Problem bei folgendem code:

Code: Alles auswählen

procedure run7Za(Para:String;var aus:TSTringlist);
var
  Thread:TBackupThread;
begin
  Thread:=TBackupThread.Create(True);
 
  Thread.files:=aus;
  Thread.cd:='7z ' + Para;
  try
    thread.Execute
  finally
    writeln('ft:',aus.Count);
  end;
end;
wenn ich ihn so auf rufe wird die anzahl einmal ausgeben und es kommt eine AV.
Danach muss ich dann das Programm mit kill Klillen.
wenn ich jetzt thread.resume aufrufe geht das zwar aber ich habe kein Inhalt in aus.

code von monta. Als ich ihn aus der Lazarus IDE kopieren wollte und hier dann einfügen wollte beendet sich jedes mal die IDE. Ich musste ihn über GEedit kopieren. Ich weiß nicht warum.

Code: Alles auswählen

procedure TBackupThread.Execute;
//----Lazarus-Wiki----:
const
  READ_BYTES = 2048;
 
var
  S: TStringList;
  M: TMemoryStream;
  P: TProcess;
  n: LongInt;
  BytesRead: LongInt;
begin
  writeln('CD:',cd);
  M := TMemoryStream.Create; BytesRead := 0;
  P := TProcess.Create(nil);P.CommandLine := cd;
  p.ShowWindow := swoHIDE; P.Options := [poUsePipes]; P.Execute;
 
  while P.Running do begin
    M.SetSize(BytesRead + READ_BYTES);     // stellt sicher daß wir Platz haben
    n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);     // versuche es zu lesen
    if n > 0 then begin Inc(BytesRead, n); end else  Sleep(100); // keine Daten, warte 100 ms
  end;
 
  // lese den letzten Teil
  repeat
    // stellt sicher daß wir Platz haben
    M.SetSize(BytesRead + READ_BYTES);
    // versuche es zu lesen
    n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
    if n > 0 then
    begin
      Inc(BytesRead, n);
    end;
  until n <= 0;
  M.SetSize(BytesRead);
  m.Position:=0;
  files.Clear;
  try
    files.LoadFromStream(m);
  finally
    S.Free;
    P.Free;
    M.Free;
  end;
 
end;
und ihn m steht auch was drin wenn ich m.savetofile sage und dann die Datei in aus lade geht es, aber leider nicht zufriedenstellen.

kann mir da jemand von euch weiter helfen ?
MFG
Michael Springwald

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

Re: Thread ? warum wird zweimal eine Procedure aufgerufen ?

Beitrag von theo »

Hast du Execute auch mit override definiert?

procedure Execute; override;

Danach mit Thread.Resume starten, nicht mit Execute.

Ausserdem tust du S.freen die du gar nicht created hast.

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

Code: Alles auswählen

TBackupThread = class(TThread)
  private
    procedure StopTimer;
    procedure Progress;
    Status: string;
  protected
    procedure Execute; override;
  public
    files:TStringlist;
    cd:String;
    Constructor Create(CreateSuspended : boolean);
  end;
ja habe ich.

Das Problem ist, sobald die procedure verlassen wird, gehen irgendwie seine Daten die in Files drin stehen weg.
in der Procedure run7z wird dann immer nur noch 0 ausgeben.
das komische ist auch:
das die writeln Anweisung nach resume zu erst ausgeführt wird und nicht danach.

bis jetzt konnte ich das Problem nicht lösen.
Ich möchte legendlich die Daten vom Memory Stream in eine TStringlist kopieren ohne sie abspeichern zu müssen.
MFG
Michael Springwald

monta
Lazarusforum e. V.
Beiträge: 2809
Registriert: Sa 9. Sep 2006, 18:05
OS, Lazarus, FPC: Linux (L trunk FPC trunk)
CPU-Target: 64Bit
Wohnort: Dresden
Kontaktdaten:

Beitrag von monta »

Ist doch normal.
Sobald die Procedure beendet ist, wird auch der Thread zerstört.
Folglich gehen natürlich die Daten verloren.

FreeOnTerminate := false; könnte abhilfe schaffen.
Dann musst du den Thread allerdings manuell zerstören.

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

es spielt keine rolle :( ob das nun auf true oder False stehe es geht so oder so nicht.

Code: Alles auswählen

procedure TForm1.run7Za(Para:String;var aus:TSTringlist);
var
  Thread:TBackupThread;
begin
  Thread:=TBackupThread.Create(False);
 
  Thread.cd:='7za ' + Para;
  Thread.files:=aus;
  Thread.Resume;
 
//  while Thread.Suspended = False do begin if Thread.Suspended = True then break; end;
// DAS HIER WIRD VOR DEM RESUME AUSGEBEN, das verstehe ich einfach nicht sobald ich die while schleife aktivere wird es wieder zweimal ausgeführt warum ?
  writeln('ft:',Thread.files.count);
end;
MFG
Michael Springwald

monta
Lazarusforum e. V.
Beiträge: 2809
Registriert: Sa 9. Sep 2006, 18:05
OS, Lazarus, FPC: Linux (L trunk FPC trunk)
CPU-Target: 64Bit
Wohnort: Dresden
Kontaktdaten:

Beitrag von monta »

Es ist doch gerade der Sinn, das die Procedure weiterläuft, während der Thread arbeitet, da hast du das Prinziep noch nicht ganz verinnerlicht.
Natürlich wird dann writeln ausgegeben, und der Therad arbeitet ja parallel weiter.

Du musst also über Synchronize aus dem Thread auf die GUI zugreifen. Das ist übrigens auch in meinem Beispiel, was ich hochgeladen hab, so gewesen ;)

Code: Alles auswählen

procedure TBackupThread.Progress;
begin
  Form1.Memo1.Lines.Add(Status);
end;
 
//und im OnExecute:
  Status := 'Ausgabe...';  //genau deshalb besitzt mein Beispielthread, denn du zitierst ja auch die Property status ;)
  Synchronize(@Progress);
P.S.: ich häng das Beispiel hier mal an, so nützt es vielleicht auch anderen, die sonst keinen Zugriff haben
Dateianhänge
prozess.tar.gz
Prozessdemo mit Thread und Gui-Output.
Aus Spaß komprimiert sich die Exe über 7z selbst zur Laufzeit.
Die Dateien files.txt enthält die Pfade der zu komprimierenden Datei(en) und kann entsprechend angepasst werden.
Und debug2.txt enthält anschließend den
(485.07 KiB) 91-mal heruntergeladen

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

naja, leider läuft der erste Thread ja weiter, dafür ist mein Programm nicht ausgelegt gewesen. Kein wunder das ist nicht richtig klappt.

Dann werde ich mir noch mal dein Beispiel anschauen müssen.

mein ziel ist es: ich möchte von aus auf die Daten im Thread drauf zu greifen.
Das ist mein ziel.

und am liebsten währe es mir, wenn die Ausführung angehalten würde, bis die kompletten Daten da sind.

wobei mir währe es lieber, mit dem Parameter -slt werden ja Blöcke ausgeben, das ich sobald ein Block ausgelesen wurde, das dann eine Funktion auf gerufen wird die sich darum kümmern. Und zwar eine Funktion in TForm.
MFG
Michael Springwald

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

Beitrag von theo »

Wieso machst du das überhaupt in einem extra Thread?
Ein Application.Processmessages in
while P.Running do
würde es vielleicht auch tun. Oder hab ich da was nicht verstanden?

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

ja das Frage ich mich gerade auch.
Er friert auch so ein. Aus welchen Grund auch immer.
ich glaube ich nehme den Thread wieder rauß.
MFG
Michael Springwald

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

ich habe jetzt den Thread wieder rauß genommen.
Es wird aber immer noch die Procedure run7a zweimal ausgefürht:

Code: Alles auswählen

procedure TForm1.run7Za(Para:String;var aus:TSTringlist);
const
  READ_BYTES = 2048;
 
var
  P: TProcess;
  n: LongInt;
  BytesRead: LongInt;
  M: TMemoryStream;
begin
  writeln('ok');
  {M := TMemoryStream.Create;
  BytesRead := 0;
  P := TProcess.Create(Form1);
  P.CommandLine := '7za ' + Para;
  p.ShowWindow := swoHIDE;
  P.Options := [poUsePipes];
  P.Execute;
 
  while P.Running do begin
    M.SetSize(BytesRead + READ_BYTES);     // stellt sicher daß wir Platz haben
    n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);     // versuche es zu lesen
    if n > 0 then begin Inc(BytesRead, n); end else  Sleep(100); // keine Daten, warte 100 ms
  end;
 
  // lese den letzten Teil
  repeat
    // stellt sicher daß wir Platz haben
    M.SetSize(BytesRead + READ_BYTES);
    // versuche es zu lesen
    n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
    if n > 0 then begin
      Inc(BytesRead, n);
    end;
  until n <= 0;
  M.SetSize(BytesRead);
  aus.clear;
  aus.LoadFromStream(m);
  P.Free;
  M.Free;}
end;
wenn ich den auskomentieren Teil wieder rein mache wird Writeln zwei mal ausgeben.
jetzt wird er nur einmal ausgeben.
kann mir jemand sagen warum ?
MFG
Michael Springwald

schnullerbacke
Beiträge: 1187
Registriert: Mi 13. Dez 2006, 10:58
OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
CPU-Target: AMD A4-6400 APU
Wohnort: Hamburg

Beitrag von schnullerbacke »

Ich würde das mal ohne Sleep versuchen. Überlass das doch einfach dem Threadsystem. Normalerweise deutet sowas auf einen 2ten Zugriff hin. Und warum du da 2mal ne Schleife aufmachst weiß ich auch nicht so recht.

Ich würde mal ne neu Klasse von TProcess oder TThread ableiten und das einlesen da veranstalten. Ist nix mehr zum lesen da beendet sich der Prozess oder Thread selbst, das wäre die logische Machart.
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.

(Ringelnatz)

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

ich habe es jetzt so:

Code: Alles auswählen

procedure TForm1.run7Za(Para:String;var aus:TSTringlist);
const
  READ_BYTES = 2048;
var
  P: TProcess;
  n: LongInt;
  BytesRead: LongInt;
  M: TMemoryStream;
begin
  writeln('ok');
  M:= TMemoryStream.Create;BytesRead := 0;
  P:= TProcess.Create(Form1);
  P.CommandLine := '7za ' + Para;
  p.ShowWindow := swoHIDE;P.Options := [poUsePipes];
  P.Execute;
 
  while P.Running do begin
    M.SetSize(BytesRead + READ_BYTES);
    n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
    if n > 0 then begin
      Inc(BytesRead, n);
    end
    else begin
      p.Terminate(-1);
      break;
    end;
//      break;
    //Sleep(100); // keine Daten, warte 100 ms
  end;
 
{ repeat
    M.SetSize(BytesRead + READ_BYTES);
    n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
    if n > 0 then begin Inc(BytesRead, n);  end;
  until n <= 0;
  M.SetSize(BytesRead);}aus.clear;aus.LoadFromStream(m);P.Free;M.Free;
  writeln('ft: ' + IntTostr(aus.count));
end;
es wieder aber immer noch 2 mal ausgeben.

Edit: durch test's konnte ich Ferstellen das es an diesem code liegen muss:

Code: Alles auswählen

repeat
    M.SetSize(BytesRead + READ_BYTES);
    n := P.Output.Read((M.Memory + BytesRead)^, READ_BYTES);
    if n > 0 then begin Inc(BytesRead, n);  end;
  until n <= 0;
ich vermute was das es mit der Größe zu tuen hat oder so.
Zuletzt geändert von pluto am Fr 20. Apr 2007, 15:08, insgesamt 1-mal geändert.
MFG
Michael Springwald

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

Beitrag von theo »

pluto hat geschrieben: es wieder aber immer noch 2 mal ausgeben.
Das kann doch gar nicht sein. Bist du sicher, dass du die Funktion nicht 2 x aufrufst?

pluto
Lazarusforum e. V.
Beiträge: 7192
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Beitrag von pluto »

ja da bin ich mir sehr sicher.
das habe ich auch erst vermutet, es ist nur bei diesem einen Archiv der Fall.
bei allen anderen Archiven geht es Prima.
Also bei kleinern !

edit:
ich habe jetzt sogar den Orgnial Code aus der wiki genommen: der gleiche Fehler. Die Procedure wird aus irgendeinen Grund zweimal aufgerufen.
MFG
Michael Springwald

schnullerbacke
Beiträge: 1187
Registriert: Mi 13. Dez 2006, 10:58
OS, Lazarus, FPC: Winux (L 1.2.xy FPC 2.6.z)
CPU-Target: AMD A4-6400 APU
Wohnort: Hamburg

Beitrag von schnullerbacke »

Dann mach mal statt:

if n > 0 then begin Inc(BytesRead, n); end;

if n > 0
then Inc(BytesRead, n)
else Break;
Humor ist der Knopf, der verhindert, daß uns der Kragen platzt.

(Ringelnatz)

Antworten