Как указано в ответе Дэвида Шварца, в многопоточном Программа raise
отправляет сигнал в поток, который вызывает raise
.
Чтобы получить сигнал, отправленный в нужный поток, в этой тестовой программе , измените raise(SIGUSR1)
до pthread_kill(thread, SIGUSR1)
. Однако, если вы хотите, чтобы указанный поток c обрабатывал SIGUSR1
при отправке во весь процесс , вам нужно использовать pthread_sigmask
, чтобы заблокировать SIGUSR1
во всех потоках. кроме того, который должен с этим справиться. (Подробнее об этом см. Ниже.)
В системах, использующих glib c, signal
устанавливает обработчик сигналов, который не блокирует прерывания звонки. Чтобы получить обработчик сигнала, который это делает, вам нужно использовать sigaction
и установить sa_flags
в значение, которое не включает SA_RESTART
. Например,
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_handler = sighandler;
sa.sa_flags = 0;
sigaction(SIGUSR1, &sa, 0);
Примечание: memset(&sa, 0, sizeof sa)
равно , а не гарантированно будет иметь тот же эффект, что и sigemptyset(&sa.sa_mask)
.
Примечание: обработчики сигналов являются глобальными для процесса , поэтому не имеет значения, какой поток вы называете sigaction
. Почти во всех случаях многопоточные программы должны делать все свои sigaction
вызовы в main
перед созданием каких-либо потоков, просто чтобы убедиться, что обработчики сигналов активны до того, как могут возникнуть какие-либо сигналы.
Сигнал может быть доставлен в поток до поток может вызвать sem_wait
. Если это произойдет, обработчик сигнала будет вызван и возвращен, а затем будет вызван sem_wait
, и он будет заблокирован навсегда. В этой тестовой программе вы можете сделать это произвольно маловероятным, увеличив длину sleep
в main
, но невозможно сделать невозможным . Это неустранимая причина.
Существует небольшое количество системных вызовов, которые атомно разблокируют сигналы во время сна, а затем снова блокируют их перед возвратом в пространство пользователя, например sigsuspend
, sigwaitinfo
и pselect
. Это системные вызовы only , для которых можно избежать этого состояния гонки.
Лучшая практика для многопоточной программы, которая имеет дело с сигналами, - выделять один поток для обработки сигналов. Чтобы это работало надежно, вы должны заблокировать все сигналы, кроме синхронных исключений ЦП (SIGABRT
, SIGBUS
, SIGFPE
, SIGILL
, SIGSEGV
, SIGSYS
и SIGTRAP
) в самом начале из main
, до создания каких-либо потоков. Затем вы устанавливаете обработчик сигналов бездействия ( с SA_RESTART
) для сигналов, которые вы хотите обрабатывать; они никогда не будут вызываться, их цель - не дать ядру убить процесс из-за действия по умолчанию SIGUSR1
или чего-либо еще. Набор сигналов, которые вам нужны, должен включать все сигналы для пользовательских прерываний: SIGHUP
, SIGINT
, SIGPWR
, SIGQUIT
, SIGTERM
, SIGTSTP
, SIGXCPU
, SIGXFSZ
. Наконец, вы создаете поток обработки сигналов, который зацикливает вызов sigwaitinfo
для соответствующего набора сигналов и отправляет сообщения остальным потокам, используя каналы или переменные условия или что угодно, кроме , но сигналов в действительности. Этот поток никогда не должен блокироваться ни в каком системном вызове, кроме sigwaitinfo
.
. В случае этой тестовой программы поток обработки сигналов будет отвечать на SIGUSR1
, вызывая sem_post(&sem)
. Это либо пробудит поток слушателя, либо приведет к тому, что поток слушателя не будет заблокирован на sem_wait
.