Python: os.read() / os.write() 在 os.pipe() 上线程安全吗?

8 投票
1 回答
8343 浏览
提问于 2025-04-15 13:10

考虑一下:

pipe_read, pipe_write = os.pipe()

现在,我想知道两件事:

(1) 我有两个线程。如果我保证只有一个线程在读取 os.read(pipe_read,n),而另一个线程只在写入 os.write(pipe_write),那么即使这两个线程同时进行,我会遇到什么问题吗?我能否按正确的顺序获取所有写入的数据?如果它们同时进行,会发生什么?是否有可能一次写入的数据被分成几部分读取,比如:

Thread 1: os.write(pipe_write, '1234567')
Thread 2: os.read(pipe_read,big_number) --> '123'
Thread 2: os.read(pipe_read,big_number) --> '4567'

或者——再考虑一下同时进行——一次 os.write(some_string) 是否总是会通过一次 os.read(pipe_read, very_big_number) 完整返回?

(2) 考虑多个线程使用 logging.handlers.FileHandler() 向管道的 pipe_write 端写入——我读过日志模块是线程安全的。这是否意味着我可以这样做而不会丢失数据?我认为我无法控制管道中数据的顺序;但这并不是一个要求。

要求:

  • 所有由某些线程在写入端写入的数据必须在读取端出来
  • 由单个 logger.info(), logger.error(), ... 写入的字符串必须保持完整。

这些要求能满足吗?

提前感谢,

Jan-Philip Gehrcke

1 个回答

9

os.reados.write 在通过 os.pipe 得到的两个文件描述符上是线程安全的,但你似乎想要的不止这些。关于 (1),是的,单次读取或写入并没有“原子性”保证——你描述的场景(一次短写入结果变成两次读取)是完全可能的。一般来说,os.whatever 只是对操作系统功能的一个简单封装,具体的功能是否能满足你的需求,还是要看操作系统本身;在这个情况下,Posix 标准并不要求操作系统保证这种“原子性”。你可以保证获取到所有写入的数据,并且顺序是正确的,但仅此而已。一次写入大量数据可能会在填满操作系统提供的缓冲区后停下来,只有在其他线程读取了一些初始数据后才会继续(当然,要小心死锁!),等等等等。

关于 (2),是的,日志模块是线程安全的,并且在某种程度上是“原子”的,也就是说,通过一次调用 logging.info、logging.warn、logging.error 等产生的数据在调用底层处理程序时是“完整的”(不过如果那个处理程序又使用了非原子的方式,比如 os.write,它可能仍然会在内核中停滞,直到底层缓冲区被清理等等,和上面说的一样)。

撰写回答