大致看了下多线程和多进程的关系 多线程里同名变量写起来容易冲突麻烦,所以就用了多进程.这个多进程用起来有点迷茫 (运行环境是 Mac)
我多进程的写发如下
p = Pool(processes=6)
p.apply_async(过程名,参数)
p.close()
p.join()
1
yngzij 2020-08-06 12:30:18 +08:00
由于 Windows 没有 fork,因此多处理模块将启动一个新的 Python 进程并导入调用模块 没有 if name == 'main' 会重复创建副本。
|
2
youthfire OP @yngzij #1 我的运行环境也是 Mac 平台,你的意思是不管运行环境如何,Python 就是这样设计的?
|
3
XiaoxiaoPu 2020-08-06 13:00:03 +08:00
@youthfire Python 版本是什么?最新的 Python 多进程有三种模式,spawn 、fork 、forkserver,不同模式行为是不一样的。Python 3.8 在 macOS 下默认模式为 spawn,子进程是一个全新的解释器。
|
4
crella 2020-08-06 13:08:02 +08:00 via Android
找个类把同名变量包起来可以吗?
|
5
lithbitren 2020-08-06 13:17:03 +08:00 1
核数不填的时候默认最大,卡很久结束没碰到过,几个进程应该时几十到几百毫秒内结束。
if name == 'main'必须加,不然会重复创建对象副本,每个进程的对象都是单独运算的。 多进程有些函数需要些全局 def,反而是副本才能正常调用,用局部或 lambda 可能会 pickle 失败,其实正常写 py 不管啥脚本都建议加 main 的判断块,就跟其他语言包进 main 函数里面一样 |
6
neoblackcap 2020-08-06 13:53:02 +08:00
@lithbitren 重复创建对象副本是什么意思?
|
7
youthfire OP @XiaoxiaoPu #3 谢谢分享,我用的 3.8.5 。
|
8
youthfire OP @lithbitren #5 谢谢指点。我也就放了 12 个简单的 web 抓取数据进程,就是都跑完了,各进程都输出结果了,然后不动了,过很久最终结束。可能的话,我晚点放完整代码上来。
|
9
XiaoxiaoPu 2020-08-06 14:19:23 +08:00
@youthfire 抓取数据的话,简单点可以用多线程的,坑少一点
|
10
imn1 2020-08-06 14:35:07 +08:00
1.不需要 main,但启动要在顶级,我也说不清楚那些术语
举例,在根建一个 fun,里面写 multiprocess,然后在其他地方传参过去调用这个 fun multiprocess 写在类方法里面,运行会报错,但按上面的写法,类方法里面调用这个 fun 则可以正常运行 2.不晓得,是不是 IO 太多? 3.processes 这个貌似只是进程数吧,chrome 可以起几十个进程呢,当然多了也是问题 我只是知其然,不知其所以然,会写会抄,但搞不清状况,说错了勿怪 非 Mac 用户 |
11
renmu123 2020-08-06 14:45:39 +08:00 via Android
如果是 io 密集型推荐使用多线程,会比多进程快那么一丢丢,多线程也是有池的,在多进程模块里,你翻一下文档
|
12
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). |
13
XiaoxiaoPu 2020-08-06 15:10:36 +08:00
「程序前不加 if name == 'main'」这种情况下,你的 python 文件就没法被 import,因为 import 隐含了运行代码,死循环了
|
14
lithbitren 2020-08-06 15:21:53 +08:00
抓数据还是建议多线程或协程,多进程开几个没啥体感,开几十几百个就知道什么叫肉眼可见的慢了,协程线程一般个人项目开多少都没啥体感,而且协程线程数据可以直接共享,不用像多进程那样考虑 pickle 的问题。
计算密集也不一定多进程,单核睿频一般能到 1.5-1.8 倍的处理速度,个人机全跑满实际也就多个 3-6 倍,但要考虑的东西复杂得多,进程间的各种传值操作也有不小开销,不是计算特别耗时的项目,还不如直接单进程跑线程协程。 |
15
youthfire OP |
16
Te11UA 2020-08-06 15:45:42 +08:00
@lithbitren 那这样的话,单进程不就利用不了多核麽?
|
17
lithbitren 2020-08-06 16:04:12 +08:00
@Te11UA 多进程麻烦啊,比如抓数据一条几秒抓回来,处理数据就几到几十毫秒,需要并行处理的机会也不一定太高,个人电脑多核计算对这几十毫秒意义不大,不如直接单进程省事,想用也可以,只是不推荐而已。
|
18
youthfire OP 晚上特地去试验了下协程,用的 gevent.joinall([gevent.spawn(函数)若干]),整体感觉就跟没用一样,比多进程慢很多. 准备用一下多线程
|
19
lithbitren 2020-08-07 11:12:52 +08:00
@youthfire 现在 Python 讲的协程基本都是官办协程了,不过 gevent 爬虫可以直接用 grequests,也不费事。
|
20
wuwukai007 2020-08-07 11:34:15 +08:00 via Android
@lithbitren grequests 挺占内存的
|
21
youthfire OP @lithbitren #19 改用了多线程池后发现效果确实不错,速度甚至更快,也没有多进程池那最后卡顿了(虽然不明白卡顿是什么原因)
|
22
phoulx 2023-02-01 12:09:57 +08:00 via iPhone
卡顿是因为 apply_async 返回 AsyncResult 对象,但是运行结果实际在 join()时才执行得到。AsyncResult 可以手动 get()结果,也可以加上 callback 参数来指定回调动作。
|