除了alk's answer:
您可以使用每線程功能以某個線程的方式選擇在某個信號傳遞時執行哪個函數的指針。
注意:信號傳送到任何線程,不會明確阻止其傳送。這不會改變這一點。您仍然需要使用pthread_kill()
或類似的機制將信號引導至特定的線程;發出或發送到進程(而不是特定線程)的信號仍將由隨機線程處理(在不阻止它的那些線程中)。
我想不出任何用例,我個人更喜歡這種方法;到目前爲止,一直有其他方式,其他方式更容易和更好。因此,如果您正在考慮爲實際應用程序實施此類功能,請退後一步並重新考慮您的應用程序邏輯。
但是,因爲該技術是可能,這裏是如何我可能會實現它:
#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;
}
我喜歡上面的,因爲它不需要並行線程,並且非常穩定和可靠的。除了由於具有預處理器邏輯來選擇使用哪種類型的原子內置插件而造成的視覺混亂之外,如果仔細觀察,它也非常簡單。
GCC 4.7及更高版本提供C++ 11-like __atomic built-ins,較老的GCC版本和其他編譯器(ICC,Pathscale,Portland Group)提供__sync legacy built-ins。用於線程本地存儲的__thread
keyword應該類似地在所有當前POSIX-y系統中可用。
如果你有一個陳舊的系統,還是堅持標準的符合性,下面的代碼應該大致相當於行爲:
#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;
}
我覺得現實生活中的等價最接近像這樣的代碼,我其實已經曾經使用過的一個例子是,我用一個實時信號(SIGRTMIN+0
)在一個線程中被阻塞,作爲反射器:它發送另一個實時信號(SIGRTMIN+1
)給多個工作線程,以中斷阻塞I/O。 (可以用一個實時信號做到這一點,但雙信號模型實現起來更簡單並且更容易維護。)
這樣的信號反射或扇出有時是有用的,並且它沒有什麼不同這種方法。儘管如此,如果有人對此感興趣,還是有足夠的差別來保證自己的問題。
信號是一個進程調用,而不是每個線程一個,如果你調用它,它會設置進程中所有線程的處理程序。信號和線程是一個複雜的話題。你最好問自己該做什麼。 –
在多線程程序中使用'signal'基本上是未定義的行爲。它在手冊中是這樣說的。 –
源代碼請。 –