2012-06-02 16 views
15

Ho un'applicazione che secondo thread chiama GetMessage() in un ciclo. Ad un certo punto il primo thread si rende conto che l'utente vuole uscire dall'applicazione e notifica il secondo thread che deve terminare. Poiché il secondo thread è bloccato su GetMessage(), il programma non si chiude mai. C'è un modo per attendere i messaggi con un timeout? Sono aperto anche ad altre idee.GetMessage con un timeout

EDIT: (ulteriori spiegazioni)

Il secondo filo corre che frammento di codice:

while (!m_quit && GetMessage(&msg, NULL, 0, 0)) 
{ 
    TranslateMessage(&msg); 
    DispatchMessage(&msg); 
} 

Il primo thread imposta m_quit su true.

+0

Attendere ... quale thread è nel ciclo in attesa di 'GetMessage()'? Il primo thread, il secondo thread o entrambi? – templatetypedef

+0

Il secondo thread esegue il ciclo GetMessage. Dovrei pubblicare del codice? – qdii

+0

Il codice aiuta sempre. Sono solo confuso perché la tua domanda fa sembrare che entrambi i thread siano bloccati. Non puoi semplicemente inviare un messaggio personalizzato che dice "devi chiudere adesso?" – templatetypedef

risposta

13

Il modo più semplice è semplicemente chiamare UINT_PTR timerId=SetTimer(NULL, NULL, 1000, NULL) prima di chiamare GetMessage. Posterà un messaggio WM_TIMER sul thread chiamante ogni secondo, quindi lo GetMessage ritornerà prontamente. Quindi, chiama KillTimer(NULL, timerId) per cancellarlo.

aggiornamento del codice Esempio:

BOOL GetMessageWithTimeout(MSG *msg, UINT to) 
{ 
    BOOL res; 
    UINT_PTR timerId = SetTimer(NULL, NULL, to, NULL); 
    res = GetMessage(msg); 
    KillTimer(NULL, timerId); 
    if (!res) 
     return FALSE; 
    if (msg->message == WM_TIMER && msg->hwnd == NULL && msg->wParam == timerId) 
     return FALSE; //TIMEOUT! You could call SetLastError() or something... 
    return TRUE; 
} 
+0

Watchdog piuttosto strano =) – Forgottn

+0

@rodrigo: +1. finora la migliore soluzione che ho visto. Aspetterò che si presentino altri candidati. – qdii

+0

@rodrigo attenzione per "msg" è un puntatore. inoltre, la struttura a punta non ha un membro 'uMsg', si chiama' message'. – qdii

-1

Sì. Prova invece a PeekMessage(). Ma penso che questo non risolva completamente i problemi.

+0

@Forgoth Chiamare 'PeekMessage()' sarebbe un'ottima soluzione se potessi accoppiarlo con 'Sleep (1000)', ma nel mio caso il secondo thread dovrebbe essere molto reattivo. +1 perché la soluzione è valida in altri contesti – qdii

4

Una cosa che si può sempre fare è inviare il thread bloccato un messaggio definito dall'utente che causerà a svegliarsi, elaborare il messaggio, per poi tornare su all'inizio del ciclo. In effetti, potrebbe essere più semplice rimuovere completamente la variabile m_quit e inviare semplicemente al thread principale un messaggio che dice "è necessario uscire ora".

Spero che questo aiuti!

+1

+1, questo aiuta. Preferisco la soluzione di Rodrigo in quanto mantiene l'intero meccanismo di smettere in un unico punto. – qdii

+1

Questo è un metodo molto più pulito. Il punto della discussione è ricevere messaggi che gli dicano cosa fare. E questo non richiede che il thread si svegli costantemente quando non c'è niente da fare. –

2

Dovresti essere in grado di inviare un messaggio di chiusura al secondo thread con PostThreadMessage.

E.g.

PostThreadMessage(threadid, WM_QUIT, 0, 0); 

Non dovrebbe essere necessario per leggere la variabile m_quit nel secondo thread, ma si dovrebbe verificare gli errori da GetMessage così come un valore di ritorno di FALSE/0 che è ciò che viene restituito se il prossimo messaggio è un messaggio di chiusura .

18

Non testato, ma è possibile provare la funzione MsgWaitForMultipleObjects senza alcun oggetto.

MsgWaitForMultipleObjects(0, NULL, FALSE, timeout, QS_ALLEVENTS); 

Se se i rendimenti WAIT_TIMEOUT è un timeout, ma se restituisce WAIT_OBJECT_0 è possibile chiamare GetMessage con la garanzia di non essere bloccato.

Ma notare le seguenti:

MsgWaitForMultipleObjects non ritorno se c'è entrata letto del tipo specificato nella coda dei messaggi dopo che il thread è chiamato una funzione per controllare la coda.

Quindi devi assicurarti che l'ultima volta che hai chiamato una delle funzioni del messaggio non ci siano messaggi lasciati in coda, o che tu abbia una specie di condizione di gara.

Probabilmente la soluzione migliore sarebbe quella di sostituire GetMessage con:

if (MsgWaitForMultipleObjects(0, NULL, FALSE, timeout, QS_ALLEVENTS) == WAIT_OBJECT_0) 
{ 
    while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) 
    { 
     //dispatch the message 
    } 
} 

Ma, come ho detto prima, non ho la prova che quindi non posso essere sicuro se funzionerà.

+1

ah che è meglio l'evento – qdii

+8

Ho appena avuto un deadlock in attesa di un messaggio ** SendMessage ** con questa routine. Dovresti utilizzare ** QS_ALLINPUT ** invece di ** QS_ALLEVENTS **, perché copre anche ** QS_SENDMESSAGE **. vedi [msdn] (http://msdn.microsoft.com/en-us/library/windows/desktop/ms684242%28v=vs.85%29.aspx) per ulteriori informazioni – 5andr0

+0

Grazie a @ 5andr0 il 'QS_ALLINPUT' era quello che risolto per me! – Noitidart