Я делаю некоторую обработку событий с C ++ и pthreads. У меня есть основной поток, который читает из определенной мной очереди событий, и рабочий поток, который заполняет очередь событий. Очередь, конечно, потокобезопасна.
Рабочий поток имеет список файловых дескрипторов и создает системный вызов epoll для получения событий этих файловых дескрипторов. Он использует epoll_wait для ожидания событий на fd.
Теперь проблема. Предполагая, что я хочу правильно завершить свое приложение, как я могу правильно отменить рабочий поток? epoll_wait
не является одной из точек отмены нитей (7) поэтому он не может правильно реагировать на pthread_cancel
,
Рабочий поток main () выглядит так
while(m_WorkerRunning) {
epoll_wait(m_EpollDescriptor, events, MAXEVENTS, -1);
//handle events and insert to queue
}
m_WorkerRunning
установлен в true
когда нить начинается и похоже, что я Можно прервать поток по настройкам m_WorkerRunning
ложь из основного потока. Проблема в том, что epoll_wait
теоретически можно ждать вечно.
Другое решение, о котором я думаю: вместо того, чтобы ждать вечно (-1), я могу ждать, например, X временных интервалов, затем правильно обрабатывать случай отсутствия событий, и если m_WorkerRunning == false
затем выйдите из цикла и полностью завершите рабочий поток. Затем основной поток устанавливает m_WorkerRunning
ложно, и спит X. Однако я не уверен в производительности такого epoll_wait, а также не уверен, что будет правильным X? 500мс? 1s? 10s?
Я хотел бы услышать некоторые опытные советы!
Более актуальная информация: события, на которых я жду /dev/input
так что технически я делаю какую-то подсистему ввода. Целевая ОС — Linux (последнее ядро) на архитектуре ARM.
Спасибо!
Вы можете отправить потоку сигнал, который прервет блокирующий вызов epoll_wait()
, Если это так, измените ваш код следующим образом:
while(m_WorkerRunning)
{
int result = epoll_wait(m_EpollDescriptor, events, MAXEVENTS, -1);
if (-1 == result)
{
if (EINTR == errno)
{
/* Handle shutdown request here. */
break;
}
else
{
/* Error handling goes here. */
}
}
/* Handle events and insert to queue. */
}
Способ добавить обработчик сигнала:
#include <signal.h>
/* A generic signal handler doing nothing */
void signal_handler(int sig)
{
sig = sig; /* Cheat compiler to not give a warning about an unused variable. */
}
/* Wrapper to set a signal handler */
int signal_handler_set(int sig, void (*sa_handler)(int))
{
struct sigaction sa = {0};
sa.sa_handler = sa_handler;
return sigaction(sig, &sa, NULL);
}
Чтобы установить этот обработчик для сигнала SIGUSR1
делать:
if (-1 == signal_handler_set(SIGUSR1, signal_handler))
{
perror("signal_handler_set() failed");
}
Отправить сигнал SIGUSR1
из другого процесса:
if (-1 == kill(<target process' pid>, SIGUSR1))
{
perror("kill() failed");
}
Чтобы процесс отправил сигнал самому себе:
if (-1 == raise(SIGUSR1))
{
perror("raise() failed");
}
ответ алка выше почти правильный. Разница, однако, очень опасна.
Если вы собираетесь отправить сигнал, чтобы проснуться epoll_wait
, никогда используйте epoll_wait. Вы должны использовать epoll_pwait
или вы можете столкнуться с гонкой, когда ваш эполл никогда не проснется.
Сигналы поступают асинхронно. Если твой SIGUSR1
прибывает после того, как вы проверили свою процедуру выключения, но прежде чем ваш цикл возвращается к epoll_wait
, тогда сигнал не прервет ожидание (так как его нет), но и программа не выйдет.
Это может быть очень вероятным или крайне маловероятным, в зависимости от того, сколько времени занимает цикл в зависимости от того, сколько времени потрачено на ожидание, но это ошибка, так или иначе.
Другая проблема с ответом алка в том, что он не проверяет Зачем ожидание было прервано. Причин может быть несколько, не связанных с вашим выходом.
Для получения дополнительной информации см. Справочную страницу для pselect
. epoll_pwait
работает аналогичным образом.
Кроме того, никогда не отправляйте сигналы потокам, используя kill
, использование pthread_kill
вместо. kill
Поведение при отправке сигналов в лучшем случае не определено. Нет гарантии, что правильный поток получит его, что может привести к прерыванию несвязанного системного вызова или к тому, что ничего не произойдет.