2009-11-15 8 views
8

Come posso attendere il completamento di un thread separato in C++?Attendere il completamento di un thread staccato in C++

Non mi interessa uno stato di uscita, voglio solo sapere se il thread è finito.

Sto provando a fornire un wrapper sincrono attorno a uno strumento asincrono di terze parti. Il problema è uno strano incidente di condizioni di gara che coinvolge un callback. La progressione è:

  1. io chiamo di terze parti, e registrare un callback
  2. quando le finiture di terze parti, che mi avvisa usando il callback - in un thread indipendente non ho alcun reale controllo.
  3. Voglio che il thread da (1) attenda fino a quando viene chiamato (2).

Voglio avvolgere questo in un meccanismo che fornisce una chiamata di blocco. Finora, ho:

class Wait { 
    public: 
    void callback() { 
    pthread_mutex_lock(&m_mutex); 
    m_done = true; 
    pthread_cond_broadcast(&m_cond); 
    pthread_mutex_unlock(&m_mutex); 
    } 

    void wait() { 
    pthread_mutex_lock(&m_mutex); 
    while (!m_done) { 
     pthread_cond_wait(&m_cond, &m_mutex); 
    } 
    pthread_mutex_unlock(&m_mutex); 
    } 

    private: 
    pthread_mutex_t m_mutex; 
    pthread_cond_t m_cond; 
    bool   m_done; 
}; 

// elsewhere... 
Wait waiter; 
thirdparty_utility(&waiter); 
waiter.wait(); 

Per quanto posso dire, questo dovrebbe funzionare, e di solito lo fa, ma a volte si blocca. Per quanto posso determinare dal corefile, la mia ipotesi per quanto riguarda il problema è questo:

  1. Quando il callback trasmette la fine del m_done, il filo di attesa si sveglia
  2. Il filo di attesa è ormai fatto qui, e Wait è distrutto. Tutti i membri di Wait vengono distrutti, inclusi il mutex e cond.
  3. Il thread di richiamata tenta di continuare dal punto di trasmissione, ma ora utilizza la memoria rilasciata, che provoca il danneggiamento della memoria.
  4. Quando il thread di richiamata tenta di tornare (oltre il livello del mio metodo di callback non valido), il programma si blocca (di solito con un SIGSEGV, ma ho visto SIGILL un paio di volte).

Ho provato un sacco di meccanismi diversi per provare a risolvere questo problema, ma nessuno di loro ha risolto il problema. Vedo ancora incidenti occasionali.

EDIT: Maggiori dettagli:

Questo fa parte di un programma in maniera massiccia multithread, in modo da creare un Wait statico non è pratico.

Ho eseguito un test, creando Wait sull'heap e filtrando deliberatamente la memoria (ad esempio, gli oggetti Wait non sono mai stati deallocati) e non si sono verificati arresti anomali. Quindi sono sicuro che sia un problema di attesa che viene presto rilasciato.

Ho anche provato un test con uno sleep(5) dopo lo sblocco in wait e anche questo non ha causato arresti anomali. Odio fare affidamento su un kludge come quello però.

EDIT: thirdParty dati:

io non credo che questo era rilevante in un primo momento, ma più ci penso, più credo che sia il vero problema:

La roba di terze parti Ho menzionato, e perché non ho alcun controllo sul thread: questo sta usando CORBA.

Quindi, è possibile che CORBA tratti un riferimento al mio oggetto più a lungo del previsto.

risposta

3

Sì, credo che ciò che stai descrivendo stia accadendo (condizioni di gara su deallocate). Un modo rapido per risolvere questo problema consiste nel creare un'istanza statica di Wait, che non verrà distrutta. Ciò funzionerà finché non è necessario avere più di un cameriere allo stesso tempo.

Si utilizzerà anche quella memoria in modo permanente, non sarà deallocato. Ma non sembra che sia troppo brutto.

Il problema principale è che è difficile coordinare le vite dei vostri costrutti di comunicazione filo tra le discussioni: si avrà sempre bisogno di almeno un costrutto comunicazione avanzi di comunicare quando è sicuro di distruggere (almeno nelle lingue senza garbage collection, come C++).

EDIT: Vedere commenti per alcune idee su conteggio dei riferimenti con un mutex globale.

+0

Sfortunatamente, questo è in un'app molto multithreaded, e vogliamo davvero separare gli oggetti Wait per ognuno - altrimenti ci rallenta troppo. – Tim

+0

Inoltre, se usiamo un Wait statico, c'è il problema di provare a coordinare quale thread deve riprendere. – Tim

+0

Ok, puoi farlo.È possibile aggiungere un campo Refcount all'oggetto Wait, protetto da un mutex globale. Inizia il conto alla rovescia al 2, e poi hai il callback e il cameriere diminuisce entrambi il refcount una volta terminato. Se il mutex globale diventa il collo di bottiglia, ci sono altre soluzioni più complicate. –

0

In base alle mie conoscenze, non esiste un modo portatile per chiedere direttamente un thread se è in esecuzione (ovvero nessuna funzione pthread_). Quello che stai facendo è il modo giusto per farlo, almeno per quanto riguarda una condizione che segnali. Se si verificano arresti anomali di cui si è certi che l'oggetto Wait è stato deallocato quando il thread che lo crea si chiude (e non un altro altro problema di chiusura imprevisto - tutto troppo comune), il problema è che è necessario sicuro che lo non sia deallocato, gestendo da un thread diverso da quello che fa la notifica. Inseriscilo nella memoria globale o allocalo dinamicamente e condividilo con quel thread. Molto semplicemente non si ha il thread in attesa di possedere la memoria per l'attesa, avere il filo che fa l'attesa proprio.

0

Stai inizializzando e distruggendo correttamente il mutex e la condizione var?

Wait::Wait() 
{ 
    pthread_mutex_init(&m_mutex, NULL); 
    pthread_cond_init(&m_cond, NULL); 
    m_done = false; 
} 

Wait::~Wait() 
{ 
    assert(m_done); 
    pthread_mutex_destroy(&m_mutex); 
    pthread_cond_destroy(&m_cond); 
} 

Assicurarsi che non si è prematuramente distruggere l'oggetto Wait - se viene distrutto in un thread, mentre l'altro thread ancora bisogno, si otterrà una condizione di competizione che probabilmente porterà a un segfault . Mi consiglia di renderlo una variabile statica globale che viene costruita sull'inizializzazione del programma (prima dello main()) e viene distrutta all'uscita del programma.

+0

sì, il mutex e il cond sono inizializzati/distrutti correttamente. In realtà sto usando classi wrapper su quelle che sono state ben testate. E sì, sono certo che Wait viene distrutto prematuramente - mentre un thread è ancora in Wait :: callback. – Tim

0

Se la tua ipotesi è corretta, il modulo di terze parti sembra essere buggato e hai bisogno di escogitare un qualche tipo di trucco per far funzionare la tua applicazione.

Statico Wait non è fattibile. Che ne dite di piscina Wait (potrebbe anche crescere su richiesta)? L'applicazione utilizza il pool di thread per l'esecuzione? Anche se ci sarà ancora la possibilità che lo stesso Wait venga riutilizzato mentre il modulo di terze parti lo sta ancora utilizzando. Ma puoi minimizzare tali possibilità facendo correttamente la coda alle Waits vuote nella tua piscina.

Disclaimer: Non sono affatto un esperto di sicurezza dei thread, quindi considera questo post come un suggerimento da un laico.

Problemi correlati