2008-10-23 7 views
8

Meine Anwendung hat mehrere Themen: 1) Haupt-Thread 2) 2 Sub-Main Threads (jeweils mit Message Loop, wie unten gezeigt), von TFQM verwendet 3) n Worker Threads (einfach Schleife, enthält Sleep())Delphi Multi-Threading Nachricht Schleife

Mein Problem ist, wenn ich meine Anwendung schließe, die Worker Threads ordnungsgemäß zu beenden, aber 1 der 2 Sub-Main Threads hängt (nie beendet), wenn ich WM_QUIT ausstellen, um sie zu schließen .


procedure ThreadProcFQM(P: Integer); stdcall; 
var 
    Msg: TMsg; 
_FQM: TFQM; 
begin 
    _FQM := Ptr(P); 
    try 
    _FQM.fHandle := AllocateHwnd(_FQM.WndProc); 

    while GetMessage(Msg, 0, 0, 0) do 
    begin 
     TranslateMessage(Msg); 
     DispatchMessage(Msg); 
    end; 

    finally 
    DeallocateHWnd(_FQM.fHandle); 
    SetEvent(_FQM.hTerminated); 
    end; 
end; 

procedure TFQM.Stop; 
begin 
    PostMessage(fHandle, WM_QUIT, 0, 0); 

    WaitForSingleObject(hTerminated, INFINITE); 
    if hThread <> INVALID_HANDLE_VALUE then 
    begin 
    CloseHandle(hThread); 
    hThread := INVALID_HANDLE_VALUE; 
    end; 
end; 

Antwort

9

ich das gleiche Problem hätte, und ich fand heraus, dass ich nicht ein ausgeblendetes Fenster Nachrichten nur empfangen erstellen soll. Threads haben bereits ein Nachrichtensystem.

Ich denke, dass Sie Ihre Windows-Handle erstellen und in fHandle speichern, aber GetMessage überprüft die Nachrichtenschleife des Threads. Daher die Nachricht PostMessage (fHandle, WM_QUIT, 0, 0); wird nie von der getmessssage empfangen.

Sie können Nachrichten mit PostThreadMessage in Ihren Thread schreiben und im Thread verwenden Sie GetMessage (CurrentMessage, 0, 0, 0). Der einzige wichtige Unterschied besteht darin, dass Sie die Nachrichtenschleife aus dem Thread zu beginnen haben durch

PeekMessage(CurrentMessage, 0, WM_USER, WM_USER, PM_NOREMOVE); 

Aufruf Sie sollen mit diesen starten, als Ihr Setup zu tun und als Ihre Schleife starten.

Der Grund, warum Sie mit der Peek-Nachricht beginnen sollten, ist sicherzustellen, dass Nachrichten, die während der Initialisierung Ihres Thread-Prozesses gesendet werden, nicht verloren gehen.

Das Seltsame ist, im Moment kann ich nicht die Referenz finden, wo ich das gelernt habe, aber meine Vermutung ist die Newsgroup-Community.

+0

Hoppla, tut mir leid mein erstes Mal hier. Ich wusste nichts über Kommentare .... Ich werde es versuchen. Vielen Dank. – Atlas

+0

Davy hat Recht: PeekMessage sollte verwendet werden, um die Nachrichtenwarteschlange zu erstellen – Remko

+0

Sie lesen es auf der [MSDN-Seite für 'PostThreadMessage'] (http://msdn.microsoft.com/en-us/library/ms644946 (VS.85) .aspx). Sie empfehlen sogar, dass der Hauptthread auf ein Ereignis wartet, das der Thread festlegt, sobald die Nachricht erstellt wurde. –

6

1) Sie brauchen nicht in AllocateHwnd innerhalb Ihres Threads. Der erste Aufruf von GetMessage erstellt eine separate Nachrichtenwarteschlange für diesen Thread. Um jedoch eine Nachricht an den Thread zu senden, sollten Sie die PostThreadMessage-Funktion verwenden.

Beachten Sie, dass zum Zeitpunkt des Aufrufs von PostThreadMessage die Warteschlange noch nicht erstellt werden konnte. Ich benutze normalerweise Konstruktion:

while not PostThreadMessage(ThreadID, idStartMessage, 0, 0) do 
    Sleep(1); 

um sicherzustellen, dass die Nachrichtenwarteschlange erstellt.

2) Für Fadenschleife endet ich meine eigene Nachricht definieren:

idExitMessage = WM_USER + 777; // you are free to use your own constant here 

3) Es besteht keine Notwendigkeit für eine separate Veranstaltung, da Sie Thread-Handle zu WaitForSingleObject Funktion übergeben können. So könnte Ihr Code wie folgt aussehen:

Beachten Sie, dass ThreadID und ThreadHandle unterschiedliche Werte sind.

4) Also, Ihr ThreadProc wird wie folgt aussehen:

procedure ThreadProcFQM; stdcall; 
var 
    Msg: TMsg; 
begin 
    while GetMessage(Msg, 0, 0, 0) 
    and (Msg.Message <> idExitMessage) do 
    begin 
    TranslateMessage(Msg); 
    DispatchMessage(Msg); 
    end; 
end; 
+0

Danke, ich werde es versuchen. – Atlas

+0

Wie unten dargelegt, sollten Sie die Nachrichtenwarteschlange mit PeekMessage erstellen: // Erzwingen der Nachrichtenwarteschlangenerstellung PeekMessage (Msg, 0, WM_USER, WM_USER, PM_NOREMOVE); – Remko

11

Wenn ich einige Probleme in Ihrem Code hinweisen ...

1) Sie sind nicht ausgegeben von AllocateHWnd überprüfen. Ja, wahrscheinlich wird es nie versagen, aber immer noch ...

2) AllocateHwnd belogs OUT von try..finally! Wenn dies fehlschlägt, sollte DeallocateHwnd nicht aufgerufen werden.

3) AllocateHwnd ist nicht threadsicher. Wenn Sie es von mehreren Threads gleichzeitig aufrufen, können Sie auf Probleme stoßen. Read more.

Wie Davy sagte, verwenden Sie MsgWaitForMultipleObjects, anstatt versteckte Nachrichtenfenster zu erstellen. Verwenden Sie dann PostThreadMessage, um Nachrichten an Thread zu senden.

Wenn ich hier einen Stecker für ein völlig kostenloses Produkt setzen kann - benutze stattdessen meine OmniThreadLibrary. Viel einfacher als direkt mit Windows Messaging zu verwirren.

+0

Gabr, danke, aber deine lib ist nur auf D2006 + beschränkt? Ich benutze D7 .... – Atlas

+0

D2007 +, eigentlich. Zeit für ein Upgrade, dann :) – gabr

+0

Gabr, deine Threading-Bibliothek sieht fantastisch aus - danke, dass du darauf hingewiesen hast. Oh, und ich würde sagen, deine Seite ist "spartanisch", nicht hässlich. – Argalatyr