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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pthread_kill()vs pthread_cancel()终止阻塞I / O的线程相关的知识,希望对你有一定的参考价值。

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

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

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

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

答案

根据你的线程库的确切实现,线程很可能甚至不会在被杀死时从poll返回 - 所以,你甚至可能无法实现你想要的。

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

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

另一答案

一个简单而干净的选择是创建一个“信号”管道。也就是说,调用pipe,获取“读取”结束的文件描述符,并将其添加到poll文件描述符列表中(使用POLLIN)。然后,每当你想要解锁在poll中等待的线程时,只需将一个字节写入管道的写端。已接收数据的管道将在被阻止的线程中以可读方式返回。您甚至可以通过改变写入字节的值来指定不同的“命令”。

(当然,在重新使用之前,您需要从管道中读取该字节。)

另一答案

没有杀死线程的事情。

命名不佳的pthread_kill函数是命名不佳的kill函数的线程模拟,它发送信号进行处理。 kill这个名字在历史上是有意义的,因为许多信号的默认动作是杀死这个过程。但是这种杀死进程的默认操作并不取决于信号是发送到进程还是特定线程 - 无论哪种方式,进程终止。

pthread_kill唯一有用的时候是你想在另一个线程上调用信号处理程序。除非您确定信号处理程序不能中断任何非异步信号安全的函数,否则信号处理程序仅限于调用异步信号安全的函数,因此甚至无法终止线程的生命周期( pthread_exit不是异步信号安全的)。

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

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

  1. 使用没有sigactionSA_RESTART安装信号处理程序(可以是无操作),这样就会导致EINTR。由于这种方法存在固有的竞争条件(如果您在输入阻塞系​​统调用之前发送信号,而不是一旦阻塞,信号将无法执行任何操作)您需要重复发送信号,指数退避为了不使目标执行时间匮乏,直到目标通过其他同步机制(POSIX信号量工作正常)确认它得到了消息。
  2. 安装一个longjmp的信号处理程序。为了安全地执行此操作,您需要控制可能发生的上下文;最简单的方法是在信号掩码中正常阻止它,只有当jmp_buf在阻塞调用周围有效时才会取消屏蔽。您调用的阻塞函数需要是异步信号安全的,并且它不需要是分配或释放资源的函数(如openclose),因为在处理信号时您将无法知道它是否已完成。当然,jmp_buf或指向它的指针需要是一个线程局部对象(_Thread_local / __thread)才能使其工作。

以上是关于pthread_kill()vs pthread_cancel()终止阻塞I / O的线程的主要内容,如果未能解决你的问题,请参考以下文章

pthread中向线程发送信号

GCC警告与std = c11 arg

显示未发生的调用堆栈的崩溃日志

如何查看一个进程中的某个线程是否存活?

帮助解释堆栈跟踪

如何调试此崩溃日志