Entwickler-Ecke

Windows API - Thread sofort starten....


Steve1024 - Do 15.09.05 14:27
Titel: Thread sofort starten....
Hi an alle,

ich hab mir ein kleines Prog geschrieben (besser DLL Dateien), welche eine volle Interprozesskommunikation ermöglichen (wie z.B. TCP nur halt lokal ohne netzwerk usw.).

Ich hab da jetzt folgendes Problem:

{Client}
Ich erstelle ein Thread in dem Constructor eines Objects (nur zum empfangen von Daten). Sende dem ServiceThread das ich mich anmelden möchte und warte auf antwort.... und warte.... und warte.... (ServerThread sendet aber)

Gut... also Problem ist das Warten... Ich hab dabei dann mal den Debugger laufen lassen und festgestellt, dass der Thread gar nicht ausgeführt wird... D.h. wird er schon... sobald ich aus dem Create drausen bin.... D.h. wenn ich im Form constructor das Object erstelle... wird der Thread erst dann ausgeführt, wenn ich da auch wieder draußen bin...

Für Consolen-Anwendung ist das also z.B völlig ungeeignet, denn wenn ich draußen bin... läufts ja ned mehr...

Weiß jemand, wie ich den Thread sofort nach dem erstellen ausführen kann... so eine art API-Funktion.... Dass also in diesem beispiel er nicht ewig wartet.... sondern auch in der lage ist eine antwort zu senden??


MrSaint - Fr 16.09.05 08:17

Wie sieht dein Thread denn aus? Abgeleitete Klasse von TThread oder einfach Funktion, oder... ? Und wie startest du den Thread? Meiner Meinung nach müsste der auch sofort starten...


MrSaint


Steve1024 - Fr 16.09.05 08:43

Ja, der ist von TThread abgeleitet.
Dazu wird der über Create gestartet (Suspend = FALSE)


Udontknow - Fr 16.09.05 09:24

Hallo!

Sobald du den Thread erstellt hast, ist es eine Sache vom Betriebssystem, diesem Rechenzeit zuzuweisen. Das kannst du nur über die Threadpriority beeinflussen. Wann der nun aber den einen oder anderen Breakpoint erreicht, ist immer noch ungewiss.

Bei Konsolenanwendungen musst du doch irgendwie dann auf Input warten. Wenn die Konsolenanwendung sich vorher beendet, scheint der Input ja dann nicht nötig zu sein.

Cu,
Udontknow


Steve1024 - Fr 16.09.05 11:33

Sagen wir mal so... meine Consolenanwendung kovertiert eine Datei in irgendwas....
Der Thread soll die Datei kovertieren und eine Schleife überwacht den Status... Welche Datei kovertiert werden soll, wird als Parameter übergeben (somit ist keine Eingabe von nöten).

Sobald keine eingabe von nöten ist, und die Schleife durchläuft, wird der Thread nicht gestartet...
D.h. der Thread wird erstellt... die Schleife kann auch den Status abfragen, welcher aber immer auf 0 bleibt, weil die Exceute Procedure nur in einer Pause aufgerufen wird...

Denkst du, dass das Problem beseitigt ist, wenn ich die Priorität von Idle auf ??was?? stelle? Denn bei Realtime, reagiert dann mein ganzes System nicht mehr, wenn ich das mache... hatte ich mal ausprobiert... deswegen habe ich das da mal sein lassen... *g*


MrSaint - Fr 16.09.05 11:47

Vielleicht solltest du es auf Normal setzen! Bei Idle ist ja klar dass es nicht funktioniert, da macht der thread ja nur was, wenn der Rechner grad so gut wie nix anderes zu tun hat! Die Priority von deinem Thread sollte midnestens so hoch sein wie von deinem restlichen Programm...



MrSaint


Steve1024 - Fr 16.09.05 11:52

OK, werd ich mal ausprobieren und melde mich dann wieder...

Danke auf jeden fall jetzt schon..


Udontknow - Fr 16.09.05 13:47

user profile iconSteve1024 hat folgendes geschrieben:
Sobald keine eingabe von nöten ist, und die Schleife durchläuft, wird der Thread nicht gestartet...
D.h. der Thread wird erstellt... die Schleife kann auch den Status abfragen, welcher aber immer auf 0 bleibt, weil die Exceute Procedure nur in einer Pause aufgerufen wird...


Da komme ich nicht mehr mit. Was für eine Pause? Vielleicht zeigst du mal ein wenig Code. Probiere es in der Schleife auch ruhig mal mit Sleep.

Cu,
Udontknow


Steve1024 - Fr 16.09.05 14:36

Also:

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:
31:
32:
33:
34:
35:
36:
program Test

  TAThread = class(TThread)
  protected
    procedure Execute; override;
  public
    Status : String;
  end;

  procedure TAThread.Exceute;
  var Q : Integer;
  Begin
    Q := 0;
    while Q < 4096 do
    begin
      Status := 'Wird ausgeführt';
      inc(Q); Slepp(100);
    end;
    Status := 'Beendet';
  end;

var Q : TAThread;
    tmpMsg : TMsg;
begin
  Q := TThread.Create(FALSE)
  try
    while Q.Status <> 'Beendet' do
    Begin
      writeln(Q.Status); // bleibt immer leer
      PeekMessage(tmpMSg,0,0,0,PM_REMOVE);
    end;
  finally
    Q.Free;
  end;
  Readln;
end;


So ungeführ würde mein Prog aussehen...
Vielleicht kann man mir da jetzt ja besser helfen...


Udontknow - Fr 16.09.05 15:54

Hallo!

1. Hast du diesen Code mal getestet, den du da hingeschrieben hast? Der strotzt nur so vor Fehlern, compilierbar ist´s nicht.

2. Wenn du 4096 mal 100 ms wartest, sind das 496 Sekunden, also über 8 Minuten. Hast du wirklich so lange gewartet?

3. Wenn Q.Status endlich "Beendet" enthält, gibst du es nicht aus, das könnte dich auch zu einer falschen Annahme geführt haben.

4. Beschäftige dich mal intensivst mit Synchronisation von Threads. Einfach so Elemente wie Strings in verschiedenen Threads abzugreifen ist nämlich nicht gut, das führt zu bösen Effekten wie AV usw.


Hier mal der halbwegs überarbeitete Code:


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:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
program Test;

{$APPTYPE CONSOLE}

uses Windows,Classes,SysUtils;

type TAThread = class(TThread)
  protected  
    procedure Execute; override;  
  public  
    Status : String;  
  end;

  procedure TAThread.Execute;
  var Q : Integer;
  Begin
    Q := 0;
    while Q < 10 do
    begin
      Status := 'Wird ausgeführt';
      inc(Q);
      Sleep(1000);
    end;
    Status := 'Hey, der Thread ist beendet!';
  end;


var Q : TAThread;
var WaitResult:Integer;
begin
  Q := TAThread.Create(FALSE);
  try
    Repeat
      WaitResult:=WaitForSingleObject(Q.Handle,1000);
      if WaitResult<>0 then
        writeln('Thread ist noch nicht beendet.');
    until WaitResult=0;

    WriteLn(Q.Status);
  finally
    Q.Free;
  end;
  Readln;
end.


Cu,
Udontknow


Steve1024 - Fr 16.09.05 19:21

Ich hab ja deswegen geschrieben, dass er so ungefähr aussieht... (hab das schnell aus meinem Kopf getippt gehabt)....

Also nun der richtige Quellcode... vielleicht sagt der dir mehr..


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:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
unit TestThread;

interface

uses Classes, Windows, Messages, SysUtils;

  TSTestObject= class
    constructor Create;
    destructor Destroy; override;
  private
    FReadThread  : TThread;
    FThreadID : Cardinal;
  published
    property ThreadID : Cardinal read FThreadID;
  end;

implementation

type
  TSTestThread= class(TThread)
    destructor Destroy; override;
  protected
    procedure Execute; override;
  end;

  destructor TSTestThread.Destroy;
  Begin
    PostThreadMessage(ThreadID,WM_DESTROY,0,0);
    inherited;
  end;

  // ========== Protected Methods ==========

  procedure TSTestThread.Execute;
  var vMsg : TMsg;
  Begin
    while not Terminated do
    Begin
      IF GetMessage(vMsg,0,0,0THEN
        case vMsg.message of
          WM_DESTROY : Terminate;
        ELSE
          {...}
          // z.B: MessageBox(0,PChar(IntToStr(vMsg.message)),nil,0);
        end;
    end;
  end;

// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

  constructor TSTestObject.Create;
  Begin
    inherited;
    FReadThread := TSTestThread.Create(FALSE);
    FThreadID := FReadThread.ThreadID;
    {...}
  end;

  destructor TSTestObject.Destroy;
  Begin
    FReadThread.Free;
    inherited;
  end;

// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

end.


Wenn ich jetzt das Object erstelle und über PostThreadMessage eine nachricht schicke... bekomme ich diese nicht...


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
  procedure Test;
  var Q : TSTestObject;
  Begin
    Q := TSTestObject.Create;
    try
      IF not PostThreadMessage(Q.ThreadID,WM_USER,0,0THEN
        MessageBox(0,'Error sending',nil,0);
    finally
      Q.Free;
    end;
  end;


Ich denke mal, dass das bei dir auch so sein könnte, oder??


Udontknow - Fr 16.09.05 19:57

Also hast du letztendlich überhaupt gar kein Problem mit Threads, sondern mit Botschaftsbehandlung. :roll:

Tja, da bin ich auch nicht so fit.

Cu,
Udontknow


Steve1024 - So 18.09.05 17:22

Nein.

es ist so, erstelle ich den Thread und sende erst später die Message, dann funktioniert es.

Also das heisst, dass ich PostThreadMessage über einen Timer laufen lasse (oder nach einer Eingabe), dann funktioniert es problemlos.

Es ist nur, wenn ich in der Procedure gleich nach dem Erstellen eine MessageSende.

Ich habe auch mit dem Debugger festgestellt, dass eben der Thread erst nach dem Ende der Procedure ausgeführt wird. Also, d.h. ich könnte machen, was ich will (egal ob jetzt mit Messages oder nicht), er wird erst nach der Procedure ausgeführt... oder???


Udontknow - So 18.09.05 19:40

Zitat:
Ich habe auch mit dem Debugger festgestellt, dass eben der Thread erst nach dem Ende der Procedure ausgeführt wird. Also, d.h. ich könnte machen, was ich will (egal ob jetzt mit Messages oder nicht), er wird erst nach der Procedure ausgeführt... oder???


Ich habe dir doch schon gesagt, daß es völlig unbestimmbar ist, wann der Thread das erste Mal Rechenzeit von Windows bekommt. Du hast sehr wohl nur ein Problem im Bereich "Botschaftsbehandlung".

Folgendes über PostThreadMessage:
Zitat:
The thread to which the message is posted must have created a message queue, or else the call to PostThreadMessage fails. Use one of the following methods to handle this situation:

· Call PostThreadMessage. If it fails, call the Sleep function and call PostThreadMessage again. Repeat until PostThreadMessage succeeds.


Cu,
Udontknow


Steve1024 - Di 20.09.05 11:56

Ok, ich werde das mal testten... danke auf jeden fall...