根据python规范,
recv()
python
Connection
的方法,(从
multiprocessing.Pipe()
返回,当管道为空且管道的另一端关闭时抛出
EOFError
(这里参考:
https://docs.python.org/3.9/library/multiprocessing.html#multiprocessing.connection.Connection.recv
)
在下面的代码中,我希望子进程在管道关闭后立即退出换句话说,我不会看到很多
still alive!
打印。
import multiprocessing as mp
import sys
import time
from multiprocessing.connection import Connection
def foo(conn: Connection):
while True:
try:
val = conn.recv()
except EOFError:
print("no val (got EOFError)")
return
print(f"got val {val}")
if __name__ == "__main__":
print(f"Version: {sys.version}")
conn_r, conn_w = mp.Pipe(duplex=False)
proc=mp.Process(target=foo, args=(conn_r,))
proc.start()
conn_r.close()
for i in range(10):
time.sleep(0.1)
conn_w.send(i)
conn_w.close()
while True:
if not proc.is_alive():
break
proc.join(timeout=0.1)
print("still alive!")
但这就是我得到的
$ python3 test_pipe.py
Version: 3.9.2 (default, Feb 28 2021, 17:03:44)
[GCC 10.2.1 20210110]
got val 0
got val 1
got val 2
got val 3
got val 4
got val 5
got val 6
got val 7
got val 8
got val 9
still alive!
still alive!
still alive!
still alive!
still alive!
still alive!
still alive!
still alive!
still alive!
still alive!
still alive!
still alive!
still alive!
still alive!
still alive!
still alive!
still alive!
still alive!
...
它在你的计算机和Python版本上的表现如何?表明 python 实现中存在错误?
你观察到的行为实际上是预期的,并且没有错误。问题在于对
Connection.recv()
的行为和管道关闭机制的理解。
EOFError
在管道的另一端关闭
并且
管道中没有更多数据时立即引发。但是,你的代码在
conn_w.close()
之前立即发送了 10 条消息。子进程仍在从管道中接收这些消息,并且在收到
EOFError
之前不会耗尽这些消息。
这就是为什么在子进程退出之前会看到多个“still alive!”消息。子进程正在占用时间来处理最后几条消息。
以下是发生的事情的细分:
- 父进程创建了一个管道并派生了一个子进程。
-
父进程关闭了它的读取器端(
conn_r.close()
),并向管道发送了 10 条消息。 -
父进程关闭了它的写入器端(
conn_w.close()
)。 此操作最终将导致EOFError
,但前提是管道为空。 - 子进程接收并打印消息。
-
在子进程处理完所有 10 条消息后,它终于在管道上遇到
EOFError
并退出。
为了获得期望的行为,你需要确保在关闭写入器端之前管道为空。
一种方法是在关闭连接之前在父进程中添加一个
conn_w.close()
调用:
# ... 前面的代码 ...
for i in range(10):
time.sleep(0.1)
conn_w.send(i)
conn_w.close() # 这将最终导致 EOFError
# ... 后面的代码 ...
通过此更改,子进程将在收到所有消息后立即收到
EOFError
,从而导致快速退出并且“still alive!”消息更少。
总之,
EOFError
的行为符合预期。该问题源于管道中剩余数据的处理。通过确保在关闭写入器端之前管道为空,你可以实现子进程的预期退出行为。