Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Variablenübergabe Createthread verständnisproblem (https://www.delphipraxis.net/106959-variablenuebergabe-createthread-verstaendnisproblem.html)

TheGame1492 19. Jan 2008 21:36


Variablenübergabe Createthread verständnisproblem
 
ahoi

Ich möchte einfach 5 Zahlen/Strings in eine Datei (test.txt) schreiben, wobei jede Zahl/String durch einen eigenen Thread in diese Datei geschrieben werden soll.

Hier mein Versuch:

Delphi-Quellcode:

procedure TForm1.Button1Click(Sender: TObject);

  function WriteStringInFile( p: pchar ):integer; stdcall;
  var
    f: textfile;
  begin
    AssignFile( f, 'test.txt' );
    Append(f);
    writeln( f, p );
    CloseFile(f);
  end;
var
  s: string;
  i: integer;
  TID: DWORD;
begin
  for i:=0 to 4 do
  begin
   s := IntToStr( i );
   TID := 123+i;
   CreateThread(nil, 0, @WriteStringInFile, pchar(s), 0, tid);
  end;
end;
Das Ergebnis in der Datei ist leider immer:

4
3
4
3
4


Wo liegt mein Fehler? ;)

himitsu 19. Jan 2008 22:06

Re: Variablenübergabe Createthread verständnisproblem
 
1: Also erstmal ist dieser Dateizugriff nicht ThreadSave

Append öffnet eine Datei und setzt den Dateizeiger auf das Dateiende.
> wenn jetzt zwischen Append und WriteLn ein anderer Thread in die Datei was reingeschrieben hat, dann stimmt die Position nicht mehr, da sich das Dateiende verschiebt, aber der nicht Dateizeiger in f.

2: CreateThread übergibst du einen Zeiger auf den String "S"
gehtst dann weiter veränderst den String (für den nächsten Thread)
also könnte (und ist mit Sicherheit auch) sobald der Thread bei WriteLn ankommt der Zeiger ungültig.
> entweder ist in dem String ein anderer Inhalt (die nächste Zahl) oder noch schlimmer, der String steht inzwischen an einer anderen Speicheradresse und du greifst auf etwas zu, was kein String mehr sein muß.


Zitat:

Zitat von TheGame1492
Ich möchte einfach 5 Zahlen/Strings in eine Datei (test.txt) schreiben, wobei jede Zahl/String durch einen eigenen Thread in diese Datei geschrieben werden soll.

warum?
bzw, was willst du mal erreichen...


Das Stringproblem kannst du aber leicht umgehn, indem du die Zahl übergibst und im Thread erst in einen String umwandelst.

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);

  function WriteStringInFile( i: integer ):integer; stdcall;
  var
    s: String;
    f: textfile;
  begin
    s := IntToStr(i);
    {hier Dateizugriff für andere Threads sperren}
    AssignFile( f, 'test.txt' );
    {kannst aber auch erst hier sperren ... Assign öffnet die Datei ja noch nicht}
    Append(f);
    writeln( f, s ); // oder einfach nur writeln( f, i );
    CloseFile(f);     // das wandelt dann sleber intern um
    {und hier Dateizugriff wieder freigeben}
  end;

var
  i: integer;
  TID: DWORD;
begin
  for i:=0 to 4 do
  begin
    CreateThread(nil, 0, @WriteStringInFile, i, 0, @tid);
    // tdi ist ein Ruckgabeparameter ... heißt, da schreibt ChreateThread was rein
    // und du kannst das auslesen (es wird also nichts an ChreateThread übergeben)
  end;
end;
außerdem muß du den Zugriff auf die Datei noch irgendwie ThreadSicher machen ... z.B. über eine Delphi-Referenz durchsuchenTCriticalSection

und wenn/da du dich mit der WinAPI CreateThread nicht so auskennst (wenn ich mir TID anseh)
> schau dir mal Delphi-Referenz durchsuchenTThread an, das hat dann auch schon einige nette Dinge zur ThreadSynchronisierung eingebaut.




PS: außerdem setzt ChreateThread nicht IsMultithreaded auf True
und wenn IsMultithreaded auf False steht, dann ist der speicherManager ebenfalls nicht ThreadSave und es könnte noch weitere Probleme geben.

sirius 19. Jan 2008 22:12

Re: Variablenübergabe Createthread verständnisproblem
 
Du übergibst einen Zeiger auf den String. Du kopierst ihn nicht bei der Übergabe. Und jetzt ist die Frage wer schneller ist. Der Thread mit dem Auslesen des Strings oder der MaintThread mit dem ändern. Und wenn der MainThread ändert, ist die Frage, wo kommt der neue String hin und wird der alte Platz überschrieben. Er könnte sogar ungültig werden, wodurch ein AV kommen müsste.

Andere Fehler, die noch kommen könnten:
1. Du öffnest die Datei zum schreiben. Falls ein anderer Thread die Datei noch offen hat, kommt es zum Fehler. Das ist halt Zufall und kann nicht vorhergesagt werden. Vielleicht kommt ein Threa mal beim schreiben ins stocken, weil die Festplatte grad anderweitig zu tun hat.
[/Roter Kasten]
2. Nimm statt CreateThread besser BeginThread. Dadurch sagst du dem Speichermanager wenigstens noch Bescheid, dass du Multithreaded arbeitest (Achtung: Dann nicht mehr stdcall).

TheGame1492 19. Jan 2008 22:41

Re: Variablenübergabe Createthread verständnisproblem
 
also ich will schon strings übergeben

die denn in die datei geschrieben werden sollen

und jeder String soll in einem eigenen Thread in die datei geschrieben werden

die Zahlen waren dabei nur als beispiel gedacht


und mit BeginThread schreibt der "kompletten wirrwarr" ;)

sirius 19. Jan 2008 22:44

Re: Variablenübergabe Createthread verständnisproblem
 
Zitat:

Zitat von TheGame1492
und mit BeginThread schreibt der nur kompletten wirrwarr ;)

Zitat:

Zitat von sirius
(Achtung: Dann nicht mehr stdcall).

Edit:
In deinem Fall würde ich dann doch zur Klasse TThread greifen. Da hast du dann gleich eine Klasse, in der du den String unterbringen kannst.
Delphi-Quellcode:
type TWriteFile=class(TThread)
       constructor create(s:string);
      protected
       procedure execute; override;
      private
       Fs:String;
end;
....


constructor TWritefile.create(s:string);
begin
  Fs:=s;
  Freeonterminate:=true;
  inherited create(false);
end;
procedure TWritefile.execute;
begin
  //Hier jetzt entweder mit CriticalSection oder mit synchronize in Datei schreiben
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ...
    TWriteFile.create(s);
  ...
end;
Andere Möglichkeit (ohne TThread) wäre, du assoziierst dir Speicher und übergibst den Pointer darauf und gibst den Speicher im Thread frei.

TheGame1492 19. Jan 2008 22:45

Re: Variablenübergabe Createthread verständnisproblem
 
jo stdcall ist raus

Ergebnis/WirrWarr ist:


,

,

sirius 19. Jan 2008 22:53

Re: Variablenübergabe Createthread verständnisproblem
 
Edit oben ;)

Das Wirrwarr entsteht durch die ganz oben genannten Gründe. Du hast vorhin nur Glück gehabt; und vielleicht auch, da Beginthread etwas länger dauert. Aber dein Thread wird ja hoffentlich nicht so kurz bleiben, sonst lohnt er sich ja nicht. Und damit wirst du zu ähnlichen Laufzeitproblemen kommen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:34 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz