问题描述
Linux 手册 pthread_kill() 有以下段落:
POSIX.1-2008 建议,如果实现检测到使用 生命周期结束后的线程 ID,pthread_kill() 应该返回错误 ESRCH。 glibc 实现返回 在可以检测到无效线程 ID 的情况下会出现此错误。 但也要注意 POSIX 说尝试使用线程 ID 生命周期已结束会产生未定义的行为,并试图 例如,在调用 pthread_kill() 时使用无效的线程 ID 可以 导致分段错误。
问题是,在检查线程 ID 是否有效和发出 pthread_kill()
之间,线程可能已终止。使用 pthread_kill()
是否本质上是不安全的,因为总是存在可以变成未定义行为的竞争条件?
如何确保线程 ID 有效?
解决方法
pthread_kill() 上的竞争条件?
当线程分离时,总是。但如果线程 ID 有效,则否。
使用 pthread_kill() 是否本质上是不安全的,因为总是存在可以变成未定义行为的竞争条件?
不,并非总是如此。
如何确保线程 ID 有效?
如果线程 ID 是在 detachstate 属性设置为 PTHREAD_CREATE_DETACHED 的情况下创建的,或者为该线程调用了 pthread_detach() 或 pthread_join(),则该线程 ID 的生命周期将在该线程终止后结束。
否则它是有效的。因此,当线程未分离或加入时,线程 ID 只是有效的,您始终可以随时使用它调用 pthread_kill()
。
通常,您应该停止使用 pthread_detach
或 pthread_join
之后的线程 ID。这就像 free()
中的 malloc()
- 您不能在 malloc()
之后使用由 free()
分配的内存。与分离或加入后不能使用线程 ID 的方式相同,线程 ID 将变得无效。只是使用 pthread_detach
它“稍后”会变得无效,但你不知道什么时候,所以你无论如何都不能使用它(好吧,除非你编写自己的同步)。它可能在调用 pthread_detach
后立即失效。如果您打算使用线程 ID 任何事情,请不要分离并且不要加入它。
使用“非活动线程”(终止的非分离非连接线程)调用 pthread_kill
是有效 - 线程 ID 仍然有效。我们可以从 pthread_kill posix 中读取:
现有实现因 pthread_kill() 的结果而异,其线程 ID 指示非活动线程(尚未分离或加入的已终止线程)。有些表示在这样的调用中成功,而另一些则给出 [ESRCH] 的错误。由于本卷 POSIX.1-2017 中线程生存期的定义涵盖了非活动线程,因此所描述的 [ESRCH] 错误在这种情况下是不合适的。特别是,这意味着应用程序不能使用 pthread_kill() 检查一个线程是否终止另一个线程。
未来方向
本标准的未来版本可能要求 pthread_kill() 在向非活动线程(尚未分离或加入的已终止线程)发送信号的情况下不会因 [ESRCH] 而失败,即使不会传递任何信号,因为该线程不再运行。
FUTURE DIRECTIONS
看起来更喜欢带有非活动线程的 pthread_kill()
应该成功并返回 0。我个人喜欢这种情况下的 ESRCH
错误。
如何确保线程 ID 有效?
您必须重新设计,以便您的代码先验知道这一点。* 任何不足之处都是 TOCTOU race (CWE-367)。
幸运的是,有很多来自interprocess kill
ing 的现有技术。进程间信号不会像 pthread_kill
那样冒未定义行为的可怕风险,但细心的编码人员认为信号回收 PID 的风险同样是不可接受的。 (和线程 ID can be recycled,too。)
* 好吧,你可以通过检查一些人为的状态来做到这一点。例如,在线程例程的最后,将 mutex-protected i_am_still_running
标志设置为 false。然后只有 pthread_kill
该线程持有互斥锁并确认它仍在运行。糟糕。