pthread_kill()vs pthread_cancel()终止为I / O阻塞的线程

时间:2018-09-18 17:21:40

标签: c++ c linux sockets pthreads

在我们的服务器代码中,我们使用poll()系统调用来监视客户端套接字。以较大的超时值调用poll()。因此,调用poll()的线程被阻止进行I / O。

根据该流程,我们有一个场景,需要终止来自另一个线程的poll()中阻塞的线程。我遇到过pthread_kill()和pthread_cancel()函数,它们可以终止为I / O阻塞的目标线程。

通过阅读手册页,这两个功能似乎都能正常工作。互联网上很少有链接表明这两种功能都使用起来很危险。

是否有其他方法可以终止为I / O阻塞的线程? 如果没有,建议使用以下哪个功能。

3 个答案:

答案 0 :(得分:2)

取决于线程库的确切实现,线程被杀死时很可能甚至不会从poll返回-因此,您甚至可能无法实现所需的目标。

您需要非常小心,不要造成内存泄漏,并且仍然很可能通过杀死拥有它的线程来创建文件描述符泄漏(请注意,与进程相反,线程资源并未“清理” ”)。

使用较短的超时时间并轮询其间的终止标志,或者使用信号来中断系统调用,然后在自己的控制下终止线程,释放所有分配的资源,通常更安全。

答案 1 :(得分:2)

An easy and clean option is to create a "signal" pipe. That is, call pipe, take the file descriptor for the "read" end and add it to your list of poll file descriptors (with POLLIN). Then, whenever you want to unblock the thread which is waiting in poll, just write a byte to the write end of the pipe. The pipe, having received data, will return as readable in the blocked thread. You can even specify different "commands" by varying the value of the byte written.

(You'll need to read the byte from the pipe before it can be re-used of course.)

答案 2 :(得分:0)

没有杀死线程的事情。

名称较差的pthread_kill函数是线程类似于名称较差的kill函数的线程,该函数将信号发送给进程。名称kill在历史上是有意义的,因为许多信号的默认操作是终止进程。但是,杀死进程的默认操作并不取决于信号是发送到进程还是特定线程-不管哪种方式,进程都会终止。

pthread_kill唯一有用的时间是当您想在另一个线程上调用信号处理程序时。除非您确定信号处理程序无法中断任何非异步信号安全的函数,否则信号处理程序仅限于调用异步信号安全的函数,因此甚至无法采取行动来终止线程的生存期( pthread_exit并非异步信号安全)。

如果您对线程最终因调用而终止感到满意,那么pthread_cancel是结束陷入阻塞操作的线程的正确方法。但是,为了安全地使用它,您需要大量使用pthread_cleanup_pushpthread_cleanup_pop

如果您不希望线程终止,则信号是您唯一的选择。您有两种选择:

  1. 使用sigaction而不使用SA_RESTART来安装信号处理程序(可以是无操作),这样会导致EINTR。由于这种方法存在固有的竞争条件(如果您在进入阻塞的系统调用之前发送信号,而不是一旦被阻塞,则信号将不会执行任何操作),因此您需要反复发送信号,具有指数补偿,以免耗尽目标执行时间,直到目标通过某种其他同步机制(POSIX信号灯运行良好)确认收到消息为止。

  2. 安装将longjmp的信号处理程序。为了安全地执行此操作,您需要控制可能发生此操作的上下文;最简单的方法是将其正常保持在信号屏蔽中,仅当jmp_buf在阻塞调用周围有效时才取消屏蔽它。您调用的阻止功能必须是异步信号安全的,并且不是分配或释放资源的功能(例如openclose),因为您将在处理信号时关于是否完成的知识不足。当然,jmp_buf或指向它的指针必须是线程局部对象(_Thread_local / __thread)才能完全起作用。

    < / li>