简体   繁体   English

取消卡在epoll_wait上的线程

[英]Cancelling thread that is stuck on epoll_wait

I'm doing some event handling with C++ and pthreads. 我正在使用C ++和pthreads进行一些事件处理。 I have a main thread that reads from event queue I defined, and a worker thread that fills the event queue. 我有一个从我定义的事件队列中读取的主线程,以及一个填充事件队列的工作线程。 The queue is of course thread safe. 队列当然是线程安全的。

The worker thread have a list of file descriptors and create an epoll system call to get events on those file descriptors. 工作线程有一个文件描述符列表,并创建一个epoll系统调用来获取这些文件描述符上的事件。 It uses epoll_wait to wait for events on the fd's. 它使用epoll_wait等待fd上的事件。

Now the problem. 现在问题。 Assuming I want to terminate my application cleanly, how can I cancel the worker thread properly? 假设我想彻底终止我的应用程序,我该如何正确取消工作线程? epoll_wait is not one of the cancellation points of pthread(7) so it cannot react properly on pthread_cancel . epoll_wait不是pthread(7)的取消点之一,所以它无法在pthread_cancel上做出正确的反应。

The worker thread main() looks like this 工作线程main()看起来像这样

while(m_WorkerRunning) {
    epoll_wait(m_EpollDescriptor, events, MAXEVENTS, -1);
    //handle events and insert to queue
}

The m_WorkerRunning is set to true when the thread starts and it looks like I can interrupt the thread by settings m_WorkerRunning to false from the main thread. 线程启动时m_WorkerRunning设置为true ,看起来我可以通过主线程将m_WorkerRunning设置为false 中断线程。 The problem is that epoll_wait theoretically can wait forever. 问题在于epoll_wait理论上可以永远等待。

Other solution I though about is: instead of waiting forever (-1) I can wait for example X time slots, then handle properly no-events case and if m_WorkerRunning == false then exit the loop and terminate the worker thread cleanly. 我解决的其他解决方案是:不是永远等待(-1)我可以等待X时隙,然后正确处理无事件情况,如果m_WorkerRunning == false则退出循环并彻底终止工作线程。 The main thread then sets m_WorkerRunning to false, and sleeps X. However I'm not sure about the performance of such epoll_wait and also not sure what would be the correct X? 主线程然后将m_WorkerRunning设置为false,并且睡眠X.但是我不确定这样的epoll_wait的性能,也不确定什么是正确的X? 500ms? 500ms的? 1s? 1秒? 10s? 10秒?

I'd like to hear some experienced advises! 我想听听一些经验丰富的建议!

More relevant information: the fd's I'm waiting events on, are devices in /dev/input so technically I'm doing some sort of input subsystem. 更多相关信息:fd我正在等待事件,是/dev/input中的设备,所以从技术上讲,我正在做某种输入子系统。 The targeted OS is Linux (latest kernel) on ARM architecture. 目标操作系统是ARM体系结构上的Linux(最新内核)。

Thanks! 谢谢!

alk's answer above is almost correct. 我上面的答案几乎是正确的。 The difference, however, is very dangerous. 然而,差异非常危险。

If you are going to send a signal in order to wake up epoll_wait , never use epoll_wait. 如果要发送信号以唤醒epoll_wait ,请不要使用epoll_wait。 You must use epoll_pwait , or you might run into a race with your epoll never waking up. 你必须使用epoll_pwait ,否则你可能会遇到epoll永远不会醒来的比赛。

Signals arrive asynchronously. 信号异步到达。 If your SIGUSR1 arrives after you've checked your shutdown procedure, but before your loop returns to the epoll_wait , then the signal will not interrupt the wait (as there is none), but neither will the program exit. 如果你的SIGUSR1在你检查了关机程序后到达,但在你的循环返回到epoll_wait ,那么信号不会中断等待(因为没有),但程序也不会退出。

This might be very likely or extremely unlikely, depending on how long the loop takes in relation to how much time is spent in the wait, but it is a bug one way or the other. 这可能是非常可能或极不可能的,这取决于循环花费多长时间与等待花费的时间有关,但这是一种错误。

Another problem with alk's answer is that it does not check why the wait was interrupted. 与ALK的回答另一个问题是,它不检查,为什么等待被中断。 It might be any number of reasons, some unrelated to your exit. 这可能有多种原因,有些原因与您的退出无关。

For more information, see the man page for pselect . 有关更多信息,请参见pselect的手册页。 epoll_pwait works in a similar way. epoll_pwait以类似的方式工作。

Also, never send signals to threads using kill . 此外,永远不要使用kill向线程发送信号。 Use pthread_kill instead. 请改用pthread_kill kill 's behavior when sending signals is, at best, undefined. 发送信号时, kill的行为充其量是不确定的。 There is no guarantee that the correct thread will receive it, which might cause an unrelated system call to be interrupted, or nothing at all to happen. 无法保证正确的线程将接收它,这可能导致不相关的系统调用被中断,或者根本不会发生任何事情。

You could send the thread a signal which would interupt the blocking call to epoll_wait() . 您可以向线程发送一个信号,该信号将阻塞调用epoll_wait() If doing so modify your code like this: 如果这样做修改你的代码如下:

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. */
}

A way to add a signal handler: 一种添加信号处理程序的方法:

#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);
}

To set this handler for the signal SIGUSR1 do: 要为信号SIGUSR1设置此处理程序,请执行以下操作:

if (-1 == signal_handler_set(SIGUSR1, signal_handler))
{
  perror("signal_handler_set() failed");
}

To send a signal SIGUSR1 from another process: 从另一个进程发送信号SIGUSR1

if (-1 == kill(<target process' pid>, SIGUSR1))
{
  perror("kill() failed");
}

To have a process send a signal to itself: 让进程向自己发送信号:

if (-1 == raise(SIGUSR1))
{
  perror("raise() failed");
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM