如何解除阻塞已删除命名管道上的线程阻塞?
Posted
技术标签:
【中文标题】如何解除阻塞已删除命名管道上的线程阻塞?【英文标题】:How to unblock a thread blocking on a deleted named pipe? 【发布时间】:2019-08-02 19:25:49 【问题描述】:我有一个 Python 程序,它将命名管道用于各种目的。 每个管道都由不同的线程管理,因此不会阻塞主线程。
假设我有一个线程阻塞了对open('in', 'rb')
的调用,其中in
是命名管道的相对路径。
如果我想关闭我的程序,我会使用这样的东西来解除我的线程与另一个线程的阻塞:
with suppress(OSError):
fd = os.open('in', O_WRONLY | O_NONBLOCK)
os.close(fd)
这只是以写入模式打开管道,以便open
上的线程阻塞可以继续,然后关闭它。我使用O_NONBLOCK
来避免在另一个线程已经终止的情况下阻塞(并忽略潜在的OSError
)。
这工作正常,直到有人决定删除命名管道 in
而我的线程阻塞在 open
上。
在这种情况下,我不能使用我的“尝试以非阻塞模式打开管道并关闭它”方法,因为管道在文件系统上不再可见(并且非阻塞打开只会创建一个全新的管道)。
除了杀死线程之外,这个问题的正确解决方案是什么? 请注意,我无法阻止其他进程删除管道,并且权限也无济于事(删除进程可以以 root 身份运行)。
【问题讨论】:
最坏的情况我可以使用非阻塞打开来轮询管道,而不是在打开它时显式阻塞,但我发现阻塞方法更优雅,因为轮询需要浪费 CPU 周期并引入了时间因素。 您的问题听起来像一个 XY 问题(到底为什么有人会首先使用命名管道?)。但是如果你设置了一个信号处理程序,发送信号将中断阻塞的 open() 系统调用,让你的程序在处理完 EINTR 错误后继续。所以你可以发送一个信号而不是那个打开/关闭技巧。 @mosvy 我必须使用命名管道(说来话长)。在 Python (3.7) 中可以做到这一点吗?文档说:“Python 信号处理程序总是在 Python 主线程中执行,即使信号是在另一个线程中接收到的。这意味着信号不能用作线程间通信的手段。” 信号处理程序不需要做任何事情;只是为了中断 open() 系统调用。请参阅“答案”(我不是 python 程序员,所以这可能不是最好的方法)。另一个想法是使用O_RDWR
打开命名管道(这将不会阻塞)——但在这种情况下,您无法通过 EOF 确定作者何时关闭了管道的末端。跨度>
似乎没有办法a)在python中获取真正的线程ID(为了向特定线程发送信号所必需的)和b)在python3中处理EINTR - 所有系统调用都将是由解释器手动重新启动(尽管在 python2 中仍然可以)。因此,唯一的解决方法是以读写模式打开命名管道(所有现代系统都支持)。
【参考方案1】:
我已经通过使用os.open('in', O_RDONLY | O_NONBLOCK)
解决了我的问题,即使管道的另一端没有写入器,它也会产生一个文件描述符。
一旦我有一个有效的文件描述符可供读取,我就可以将它提供给select()
系统调用以阻塞,直到有东西要读取。
为了处理“如果有人在我阻塞时从文件系统中删除管道怎么办”问题,我使用了pipe()
系统调用来获取一个未命名的管道(Python 版本只产生 2 个文件管道两端的描述符)。
我还将这个未命名管道的读取描述符提供给select()
调用,所以任何时候我想停止我的程序,我只需通过写入未命名管道的写入描述符来解除阻塞select()
,而不管发生了什么使用命名管道。
【讨论】:
以上是关于如何解除阻塞已删除命名管道上的线程阻塞?的主要内容,如果未能解决你的问题,请参考以下文章