Oltre a alk's answer:
È possibile utilizzare una funzione per thread puntatore per scegliere quale funzione viene eseguita quando viene consegnato un determinato segnale, in modo per-thread.
Nota: i segnali vengono consegnati a qualsiasi thread che non blocca esplicitamente la consegna. Questo non lo cambia. È comunque necessario utilizzare pthread_kill()
o meccanismi simili per indirizzare il segnale a un thread specifico; i segnali che vengono generati o inviati al processo (invece di un thread specifico), saranno comunque gestiti da un thread casuale (tra quelli che non lo bloccano).
Non riesco a pensare a nessun caso d'uso in cui preferirei personalmente questo approccio; finora c'è sempre stato un altro modo, qualcos'altro più facile e migliore.Quindi, se stai pensando di implementare qualcosa di simile per un'applicazione reale, ti preghiamo di fare un passo indietro e di riconsiderare la logica dell'applicazione.
Ma, dal momento che la tecnica è possibile , ecco come potrei implementarlo:
#include <signal.h>
/* Per-thread signal handler function pointer.
* Always use set_thread_SIG_handler() to change this.
*/
static __thread void (*thread_SIG_handler)(int, siginfo_t *, void *) = (void *)0;
/* Process-wide signal handler.
*/
static void process_SIG_handler(int signum, siginfo_t *info, void *context)
{
void (*func)(int, siginfo_t *, void *);
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
func = __atomic_load_n(&thread_SIG_handler, __ATOMIC_SEQ_CST);
#else
func = __sync_fetch_and_add(&thread_SIG_handler, (void *)0);
#endif
if (func)
func(signum, info, context);
}
/* Helper function to set new per-thread signal handler
*/
static void set_thread_SIG_handler(void (*func)(int, siginfo_t *, void *))
{
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
__atomic_store_n(&thread_SIG_handler, func, __ATOMIC_SEQ_CST);
#else
void (*oldfunc)(int, siginfo_t *, void *);
do {
oldfunc = thread_SIG_handler;
} while (!__sync_bool_compare_and_swap(&thread_SIG_handler, oldfunc, func));
#endif
}
/* Install the process-wide signal handler.
*/
int install_SIG_handlers(const int signum)
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_sigaction = process_SIG_handler;
act.sa_flags = SA_SIGACTION;
if (sigaction(signum, &act, NULL))
return errno;
return 0;
}
Mi piace il sopra, perché non richiede pthreads, ed è molto robusta e affidabile. A parte il caos visivo dovuto alla logica del preprocessore per selezionare quale stile di built-in atomico vengono usati, è anche molto semplice, se si guarda attentamente.
GCC 4.7 e versioni successive forniscono C++ 11-like __atomic built-ins, versioni GCC precedenti e altri compilatori (ICC, Pathscale, Portland Group) forniscono __sync legacy built-ins. Analogamente, lo __thread
keyword per l'archiviazione locale dei thread dovrebbe essere disponibile in tutti i sistemi POSIX-y correnti.
Se si dispone di un sistema arcaico, o insistere su conformità agli standard, il seguente codice dovrebbe avere un comportamento più o meno equivalente:
#include <pthread.h>
#include <signal.h>
#include <errno.h>
static pthread_key_t thread_SIG_handler_key;
static void process_SIG_handler(int signum, siginfo_t *info, void *context)
{
void (*func)(int, siginfo_t *, void *);
*((void **)&func) = pthread_getspecific(thread_SIG_handler_key);
if (func)
func(signum, info, context);
}
static int set_thread_SIG_handler(void (*func)(int, siginfo_t *, void *))
{
sigset_t block, old;
int result;
sigemptyset(&block);
sigaddset(&block, SIG); /* Use signal number instead of SIG! */
result = pthread_sigmask(SIG_BLOCK, &block, &old);
if (result)
return errno = result;
result = pthread_setspecific(thread_SIG_handler_key, (void *)func);
if (result) {
pthread_sigmask(SIG_SETMASK, &old, NULL);
return errno = result;
}
result = pthread_sigmask(SIG_SETMASK, &old, NULL);
if (result)
return errno = result;
return 0;
}
int install_SIG_handlers(const int signum)
{
struct sigaction act;
int result;
result = pthread_key_create(&thread_SIG_handler_key, NULL);
if (result)
return errno = result;
sigemptyset(&act.sa_mask);
act.sa_sigaction = process_SIG_handler;
act.sa_flags = SA_SIGACTION;
if (sigaction(signum, &act, NULL))
return errno;
return 0;
}
penso che il più vicino equivalente vita reale a codice come questo che ho in realtà mai usato, è quello in cui ho usato un segnale in tempo reale (SIGRTMIN+0
) bloccato in tutto tranne un thread, come un riflettore: ha inviato un altro segnale in tempo reale (SIGRTMIN+1
) a un numero di thread di lavoro, per interrompere l'I/O di blocco. (È possibile fare questo con un unico segnale in tempo reale, ma il modello a due segnale è più semplice da implementare e facile da mantenere.)
Tale riflessione del segnale o fanout volte è utile, e non è molto diverso da questo approccio. Abbastanza diverso da giustificare la sua domanda, però, se qualcuno è interessato.
il segnale è una chiamata per processo, non una per thread, se la si chiama imposta il gestore per tutti i thread nel processo. Segnali e thread è un argomento complesso. Potresti stare meglio chiedendo cosa tu realmente cosa fare. –
L'uso di 'signal' in un programma con multithreading è essenzialmente un comportamento non definito. Lo dice nel manuale. –
Codice sorgente per favore. –