7

我正在使用 C++ 和 pthreads 进行一些事件处理。我有一个从我定义的事件队列中读取的主线程,以及一个填充事件队列的工作线程。队列当然是线程安全的。

工作线程有一个文件描述符列表,并创建一个 epoll 系统调用来获取这些文件描述符上的事件。它使用 epoll_wait 来等待 fd 上的事件。

现在的问题。假设我想干净地终止我的应用程序,我怎样才能正确取消工作线程?epoll_wait不是pthread(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通过从主线程设置为 false 来中断线程。问题是epoll_wait理论上可以永远等待。

我想到的其他解决方案是:我可以等待例如 X 个时隙,而不是永远等待(-1),然后正确处理无事件情况,如果m_WorkerRunning == false然后退出循环并干净地终止工作线程。然后主线程设置m_WorkerRunning为 false,并休眠 X。但是我不确定这种 epoll_wait 的性能,也不确定正确的 X 是什么?500毫秒?1秒?10s?

我想听听一些有经验的建议!

更多相关信息:我正在等待事件的 fd 是设备,/dev/input所以从技术上讲,我正在做某种输入子系统。目标操作系统是基于 ARM 架构的 Linux(最新内核)。

谢谢!

4

2 回答 2

12

上面的alk回答几乎是正确的。然而,这种差异是非常危险的。

如果您要发送信号以唤醒epoll_wait,请不要使用 epoll_wait。您必须使用epoll_pwait,否则您可能会陷入一场比赛,而您的 epoll 永远不会醒来。

信号异步到达。如果您SIGUSR1在检查关闭程序之后到达,但在循环返回之前epoll_wait,则信号不会中断等待(因为没有等待),但程序也不会退出。

这可能非常有可能或极不可能,具体取决于循环花费的时间与等待所花费的时间相关,但无论哪种方式,它都是一个错误。

alk 回答的另一个问题是它不检查等待被中断的原因。这可能是多种原因,其中一些与您的退出无关。

有关更多信息,请参见pselect. epoll_pwait以类似的方式工作。

此外,永远不要使用kill. 改为使用pthread_killkill发送信号时的行为充其量是未定义的。无法保证正确的线程会接收到它,这可能会导致不相关的系统调用被中断,或者根本没有发生任何事情。

于 2014-05-22T16:01:01.823 回答
2

您可以向线程发送一个信号,该信号会中断对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");
}
于 2013-09-07T13:50:21.760 回答