作者zerof (猫橘毛发呆雕像)
看板Python
标题Re: [问题] multiprocessing 的 Pool.map
时间Tue Apr 25 00:51:42 2017
前文 43
code snippet:
https://gist.github.com/lanfon72/5ebb60ba261fe9f5cc4bd6ca54480adb
先澄清几个问题:
a. 跳下去看了一下 Pool 的 code 顺便测了一下 sys.stdout ,发现是应该是没有
racing condition 的问题,请忘了这件事....QQ
b. print 实际上是写进 sys.stdout 物件(IO),我没搞错的话会在 newline 的时候
强制 flush 。
c. sys.stdout 没有 flush 的时候是存在 sys.stdout 里面, flush 的时候输出的
点是共用的(user screen or file).
d. subprocess 和 process 是不同的东西...
: → zerof: 不是 04/23 01:20
: → zerof: print 预设的 file = sys.stdout 是共用的, 设 flush=True 04/23 01:49
: → zerof: 可以解决这个 racing condition 04/23 01:49
: → zps: 请问是否可以想成,print 已执行,但还来不及输出 04/23 10:45
: → zps: 程式就结束了,所以若有加上 join 会有效 04/23 10:46
: → zps: 可是为何 print(x) 仍有效呢? 04/23 10:47
见 a, b & c,後来发现 sys.stdout 物件不是共用的...QQ
: → s860134: zerof 的意思是指说当有一个以上的行程同时跑到 print 04/23 12:11
: → s860134: 时,会变成序列化的输入,造成看起来是 blocking 04/23 12:12
: → s860134: 和你有没有加 join 应该是没关系~ 04/23 12:13
: → zps: 印出不连续这部分我了解,但我的问题主要是印不到30个就结束 04/23 14:00
: → zps: 理论上,pool.map 应该都要等到 subprocess 跑完才会结束 04/23 14:01
: → zps: 但我实际 run 的结果,有时却是不到30个就结束了 04/23 14:02
: → zps: 但 print(x) 却可行,後来试过加上 flush 也是可行的 04/23 14:02
: → s860134: 你可以做一个实验,每个 process 都印自己的 pid 04/23 20:39
: → s860134: os.getpid() 个人猜测是发生同时写入相互覆盖 04/23 20:40
: → zerof: 参考 #1OzP7wJp (Soft_Job) 04/23 21:21
实际上它是「结束时没有被舍弃的输出」,你可以跑一次上面的 code ,会发现 print
输出的顺序是 pool.map, print("stdout:"...), 最後才是 processes 要印出来的东
西。
: 1. 没印出任何东西
: 2. 4376 4376 4376 (印出三个 pid)
: 3. 5772 5772 (印出二个 pid)
:
: 就我个人的理解,因 print 会先 buffer,之後才会一并显示在萤幕上
: 2 & 3 应该是相互覆盖所造成的,但若是相互覆盖造成的
: 理论上改为以下的 code 应该也是同样的情况,加上 close() & join()
:
: 却可以正常显示五个 pid,这里跟我前面的推论矛盾了
: 而 1 的部分,完全没印出东西,也是我觉得纳闷的地方
在没有 pool.close() & pool.join() 的情况下, Pool spawn 过的 Processes 实际
上会在 mainThread 结束的时候执行 terminate ,这也是为什麽 print 在没有使用
flush=True 的输出会在最後的原因。
Pool 并没有用 atexit.register 去清那些会在 interpreter terminate 被强制终止
然後回收的资料,所以 1. , 2. & 3. 的情况都是正常的。
简单来说:
1) 没有被 flush 的资料会被暂存在 sys.stdout object 内
2) Process 会在正常 exit 的时候把 sys.stdout 里面的东西 flush 掉
3) interpreter terminate 的时候不保证资料完整性
你可以试着把上面的 code 第 22 行 uncomment 之後跑看看就会知道我在说什麽了...
((总之忘了 racing condition 吧真是太误会了QQ
((难怪我测了两种 Lock 都没用果然是走错路...
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 122.100.76.218
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/Python/M.1493052706.A.C43.html
1F:→ zerof: 感恩这问题让我领 500.......顺便又上了一课QQ 04/25 00:52
2F:推 zps: 感谢Orz,所以不同的 process 可能会有不同的 sys.stdout 04/25 21:31
3F:→ zps: 然後 terminate 时,会随机挑选其中之一进行 print 04/25 21:33
4F:→ zps: 因为 uncomment 22行後,会看到三种结果都出现 04/25 21:33
5F:→ zps: 但若是有加上 close() & join() 则会全部清出 04/25 21:34
6F:→ zps: 请问我可以这样理解吗? 04/25 21:34
7F:→ zerof: 用 close() & join() 是确保 Processes 会正常结束,所以在 04/25 22:21
8F:→ zerof: sys.stdout 里面的资料会被 flush 04/25 22:21
9F:→ zerof: interpreter terminate 的时候急着下庄所以不会等 Process 04/25 22:22
10F:→ zerof: 正常终止 04/25 22:22
11F:推 zps: 所以运气好的 process 会在下庄前印出东西 04/25 22:40
12F:→ zps: 有时甚至没有 process 赶上下庄前,就结束了,所以没东西印出 04/25 22:40
13F:→ zps: 改一下说法,运气好的下庄前可以正常结束,所以会 flush 04/25 22:41
14F:→ zps: 感谢您的详细说明 04/25 22:43
15F:推 s860134: 很有趣! 刚测了一下,真的只要不 print 到 \n 04/26 00:14
16F:→ s860134: 就完全不会去清子行程的 stdout,在主行程结束才去清QQ 04/26 00:16