2014-09-22 21 views
6

Quando si utilizzano i thread posix, esiste un modo per "proteggere" il thread principale dagli errori (come i riferimenti null, la divisione per zero, ecc.) Causati dai thread worker. Per "thread di lavoro" intendo un thread di posix creato da pthread_create().Protezione del thread principale dagli errori nella thread di lavoro

Purtroppo, non siamo in grado di utilizzare le eccezioni - in modo da non "cattura", ecc

Ecco il mio programma di test (C++):

void* workerThreadFunc(void* threadId) { 
    int* a = NULL; 
    *a = 5; //Error (segmentation fault) 
    pthread_exit(NULL); 
} 

int main() { 
    cout << "Main thread start" << endl; 

    pthread_t workerThread; 
    pthread_attr_t attr; 
    pthread_attr_init(&attr); 
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 
    pthread_create(&workerThread, &attr, workerThreadFunc, (void*)0); 
    pthread_join(workerThread, NULL); 

    cout << "Main thread end" << endl; 
} 

Nell'esempio di cui sopra, l'errore causato dalla workerThread volontà terminare l'intero programma. Ma vorrei che il thread principale continuasse a funzionare nonostante questo errore. È possibile raggiungere questo obiettivo?

+0

'Sfortunatamente, non possiamo usare eccezioni - quindi non "prendere", ecc. - certo che puoi. L'eccezione verrà generata nel thread che lo ha generato. –

+0

Il modo migliore per "proteggere" gli altri thread da eccezioni/errori per correggerli in modo che non accadano o vengano rilevati e gestiti/registrati/qualsiasi cosa. –

+0

Nota: nell'esempio sopra, stai tentando di unirti a un thread che non è stato ancora creato. –

risposta

8

Mi sembra che dovresti utilizzare più processi, non thread. I processi indipendenti sono automaticamente protetti da questo tipo di errori che si verificano in altri processi.

È possibile utilizzare pipe o memoria condivisa (o altre forme di IPC) per passare i dati tra thread, che ha l'ulteriore vantaggio di condividere solo la memoria che si intende condividere, quindi un bug nel "thread" di lavoro non può stomp sullo stack del "thread" principale, perché è un processo separato con uno spazio indirizzo separato.

I thread possono essere utili, ma presentano diversi svantaggi, a volte l'esecuzione in processi separati è più appropriata.

+0

Per "processo" intendi per esempio le fork()? – Martin

+0

Sì, 'fork()' i nuovi processi figlio dal master, e 'exec()' un diverso eseguibile per i worker, o semplicemente continuano a eseguire lo stesso eseguibile ma i figli sono eseguiti in modalità "worker". –

4

Assumendo che il sistema utilizza segnali in un POSIX sorta di un modo (che comunque potrebbe cadere sotto il dominio "senza eccezioni"), quindi POSIX dice:

Al momento della generazione, una determinazione deve essere fatto se il segnale è stato generato per il processo o per un thread specifico all'interno del processo. I segnali generati da un'azione attribuibile a un determinato thread, ad esempio un errore hardware, devono essere generati per il thread che ha causato la generazione del segnale.

modo da poter gestire SIGSEGV, SIGFPE, ecc su una base per pthread (ma si noti che è possibile impostare solo una funzione di gestore di segnale per l'intero processo). Quindi, è possibile "proteggere" il processo dall'essere fermato da un guasto in un singolo pthread ... fino a un certo punto. Il problema, naturalmente, è che potresti trovare molto difficile stabilire quale sia lo stato del processo - il pthread fallito e tutti gli altri pthreads. Il pthread fallito potrebbe contenere un numero di mutex. Il pthread fallito potrebbe lasciare alcune strutture dati condivise in un pasticcio. Chissà che tipo di groviglio ci sono dentro - a meno che i pthread siano essenzialmente indipendenti. Potrebbe essere possibile fare in modo che altri pthread chiudano "con grazia" ... piuttosto che schiantarsi e bruciare. Potrebbe, alla fine, essere più sicuro fermare tutti i pthread morti, piuttosto che cercare di continuare in uno stato meno che ben definito. Dipenderà interamente dalla natura dell'applicazione.

Niente è inutile ... i thread possono comunicare tra loro più facilmente dei processi e costano meno per l'avvio e l'arresto - i processi sono meno vulnerabili al fallimento di altri processi.

5

L'unico modo che posso pensare di fare questo sta registrando un gestore di segnale, che potrebbe invece di interrompere il programma, annullare il filo attualmente in esecuzione, qualcosa di simile:

void handler(int sig) 
{ 
    pthread_exit(NULL); 
} 

signal(SIGSEGV, handler); 

Si noti, tuttavia, questo è non sicuro come pthread_exit non è elencato come una delle chiamate di sistema sicure all'interno di un gestore di segnale. Potrebbe funzionare e potrebbe non esserlo, a seconda del SO che stai utilizzando, e del segnale che stai gestendo.

+0

Grazie. Questo sembra fare il trucco, almeno per ora in un semplice test. Avrò bisogno di ricercare i problemi di sicurezza di cui parli. – Martin

+0

Sembra che pthread_exit causi un SIGABRT se un oggetto è stato creato su quel thread stack e quell'oggetto ha un distruttore definito. ha catturato SIGABRT (se li ha catturati nel siggerler), oppure un crash del programma completo, ad esempio se dichiaro un oggetto di una struttura molto semplice, si bloccherà se quell'oggetto ha un distruttore definito, anche solo ~ A() {}. Ma senza questo distruttore definito, non viene inviato alcun SIGABRT.Il problema si verifica anche se dichiaro ad esempio un vettore . – Martin

Problemi correlati