对 Python 里多进程池用法有点迷茫

2020-08-06 12:08:09 +08:00
 youthfire

大致看了下多线程和多进程的关系 多线程里同名变量写起来容易冲突麻烦,所以就用了多进程.这个多进程用起来有点迷茫 (运行环境是 Mac)

我多进程的写发如下

p = Pool(processes=6)
p.apply_async(过程名,参数)
p.close()
p.join()
3401 次点击
所在节点    Python
22 条回复
yngzij
2020-08-06 12:30:18 +08:00
由于 Windows 没有 fork,因此多处理模块将启动一个新的 Python 进程并导入调用模块 没有 if name == 'main' 会重复创建副本。
youthfire
2020-08-06 12:53:36 +08:00
@yngzij #1 我的运行环境也是 Mac 平台,你的意思是不管运行环境如何,Python 就是这样设计的?
XiaoxiaoPu
2020-08-06 13:00:03 +08:00
@youthfire Python 版本是什么?最新的 Python 多进程有三种模式,spawn 、fork 、forkserver,不同模式行为是不一样的。Python 3.8 在 macOS 下默认模式为 spawn,子进程是一个全新的解释器。
crella
2020-08-06 13:08:02 +08:00
找个类把同名变量包起来可以吗?
lithbitren
2020-08-06 13:17:03 +08:00
核数不填的时候默认最大,卡很久结束没碰到过,几个进程应该时几十到几百毫秒内结束。
if name == 'main'必须加,不然会重复创建对象副本,每个进程的对象都是单独运算的。
多进程有些函数需要些全局 def,反而是副本才能正常调用,用局部或 lambda 可能会 pickle 失败,其实正常写 py 不管啥脚本都建议加 main 的判断块,就跟其他语言包进 main 函数里面一样
neoblackcap
2020-08-06 13:53:02 +08:00
@lithbitren 重复创建对象副本是什么意思?
youthfire
2020-08-06 14:13:41 +08:00
@XiaoxiaoPu #3 谢谢分享,我用的 3.8.5 。
youthfire
2020-08-06 14:17:28 +08:00
@lithbitren #5 谢谢指点。我也就放了 12 个简单的 web 抓取数据进程,就是都跑完了,各进程都输出结果了,然后不动了,过很久最终结束。可能的话,我晚点放完整代码上来。
XiaoxiaoPu
2020-08-06 14:19:23 +08:00
@youthfire 抓取数据的话,简单点可以用多线程的,坑少一点
imn1
2020-08-06 14:35:07 +08:00
1.不需要 main,但启动要在顶级,我也说不清楚那些术语
举例,在根建一个 fun,里面写 multiprocess,然后在其他地方传参过去调用这个 fun
multiprocess 写在类方法里面,运行会报错,但按上面的写法,类方法里面调用这个 fun 则可以正常运行
2.不晓得,是不是 IO 太多?
3.processes 这个貌似只是进程数吧,chrome 可以起几十个进程呢,当然多了也是问题

我只是知其然,不知其所以然,会写会抄,但搞不清状况,说错了勿怪
非 Mac 用户
renmu123
2020-08-06 14:45:39 +08:00
如果是 io 密集型推荐使用多线程,会比多进程快那么一丢丢,多线程也是有池的,在多进程模块里,你翻一下文档
XiaoxiaoPu
2020-08-06 15:07:23 +08:00
spawn 、forkserver 模式下,进程的 run 函数和参数需要进程间通信来传递,这个过程会用 pickle 来序列化、反序列化,所以 run 函数和参数会受 pickle 的限制,pickle 支持如下数据类型

* None, True, and False
* integers, floating point numbers, complex numbers
* strings, bytes, bytearrays
* tuples, lists, sets, and dictionaries containing only picklable objects
* functions defined at the top level of a module (using def, not lambda)
* built-in functions defined at the top level of a module
* classes that are defined at the top level of a module
* instances of such classes whose __dict__ or the result of calling __getstate__() is picklable (see section Pickling Class Instances for details).
XiaoxiaoPu
2020-08-06 15:10:36 +08:00
「程序前不加 if name == 'main'」这种情况下,你的 python 文件就没法被 import,因为 import 隐含了运行代码,死循环了
lithbitren
2020-08-06 15:21:53 +08:00
抓数据还是建议多线程或协程,多进程开几个没啥体感,开几十几百个就知道什么叫肉眼可见的慢了,协程线程一般个人项目开多少都没啥体感,而且协程线程数据可以直接共享,不用像多进程那样考虑 pickle 的问题。
计算密集也不一定多进程,单核睿频一般能到 1.5-1.8 倍的处理速度,个人机全跑满实际也就多个 3-6 倍,但要考虑的东西复杂得多,进程间的各种传值操作也有不小开销,不是计算特别耗时的项目,还不如直接单进程跑线程协程。
youthfire
2020-08-06 15:44:43 +08:00
@imn1 @renmu123 @XiaoxiaoPu 感谢分享心得和理解
@lithbitren 说得很量化,我会试试线程或协程,谢谢
Te11UA
2020-08-06 15:45:42 +08:00
@lithbitren 那这样的话,单进程不就利用不了多核麽?
lithbitren
2020-08-06 16:04:12 +08:00
@Te11UA 多进程麻烦啊,比如抓数据一条几秒抓回来,处理数据就几到几十毫秒,需要并行处理的机会也不一定太高,个人电脑多核计算对这几十毫秒意义不大,不如直接单进程省事,想用也可以,只是不推荐而已。
youthfire
2020-08-06 22:27:08 +08:00
晚上特地去试验了下协程,用的 gevent.joinall([gevent.spawn(函数)若干]),整体感觉就跟没用一样,比多进程慢很多. 准备用一下多线程
lithbitren
2020-08-07 11:12:52 +08:00
@youthfire 现在 Python 讲的协程基本都是官办协程了,不过 gevent 爬虫可以直接用 grequests,也不费事。
wuwukai007
2020-08-07 11:34:15 +08:00
@lithbitren grequests 挺占内存的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/696109

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX