V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
youthfire
V2EX  ›  Python

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

  •  
  •   youthfire · 2020-08-06 12:08:09 +08:00 · 3355 次点击
    这是一个创建于 1555 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    我多进程的写发如下

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

    我只是知其然,不知其所以然,会写会抄,但搞不清状况,说错了勿怪
    非 Mac 用户
    renmu123
        11
    renmu123  
       2020-08-06 14:45:39 +08:00 via Android
    如果是 io 密集型推荐使用多线程,会比多进程快那么一丢丢,多线程也是有池的,在多进程模块里,你翻一下文档
    XiaoxiaoPu
        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).
    XiaoxiaoPu
        13
    XiaoxiaoPu  
       2020-08-06 15:10:36 +08:00
    「程序前不加 if name == 'main'」这种情况下,你的 python 文件就没法被 import,因为 import 隐含了运行代码,死循环了
    lithbitren
        14
    lithbitren  
       2020-08-06 15:21:53 +08:00
    抓数据还是建议多线程或协程,多进程开几个没啥体感,开几十几百个就知道什么叫肉眼可见的慢了,协程线程一般个人项目开多少都没啥体感,而且协程线程数据可以直接共享,不用像多进程那样考虑 pickle 的问题。
    计算密集也不一定多进程,单核睿频一般能到 1.5-1.8 倍的处理速度,个人机全跑满实际也就多个 3-6 倍,但要考虑的东西复杂得多,进程间的各种传值操作也有不小开销,不是计算特别耗时的项目,还不如直接单进程跑线程协程。
    youthfire
        15
    youthfire  
    OP
       2020-08-06 15:44:43 +08:00
    @imn1 @renmu123 @XiaoxiaoPu 感谢分享心得和理解
    @lithbitren 说得很量化,我会试试线程或协程,谢谢
    Te11UA
        16
    Te11UA  
       2020-08-06 15:45:42 +08:00
    @lithbitren 那这样的话,单进程不就利用不了多核麽?
    lithbitren
        17
    lithbitren  
       2020-08-06 16:04:12 +08:00
    @Te11UA 多进程麻烦啊,比如抓数据一条几秒抓回来,处理数据就几到几十毫秒,需要并行处理的机会也不一定太高,个人电脑多核计算对这几十毫秒意义不大,不如直接单进程省事,想用也可以,只是不推荐而已。
    youthfire
        18
    youthfire  
    OP
       2020-08-06 22:27:08 +08:00
    晚上特地去试验了下协程,用的 gevent.joinall([gevent.spawn(函数)若干]),整体感觉就跟没用一样,比多进程慢很多. 准备用一下多线程
    lithbitren
        19
    lithbitren  
       2020-08-07 11:12:52 +08:00
    @youthfire 现在 Python 讲的协程基本都是官办协程了,不过 gevent 爬虫可以直接用 grequests,也不费事。
    wuwukai007
        20
    wuwukai007  
       2020-08-07 11:34:15 +08:00 via Android
    @lithbitren grequests 挺占内存的
    youthfire
        21
    youthfire  
    OP
       2020-08-07 11:55:46 +08:00 via iPhone
    @lithbitren #19 改用了多线程池后发现效果确实不错,速度甚至更快,也没有多进程池那最后卡顿了(虽然不明白卡顿是什么原因)
    phoulx
        22
    phoulx  
       2023-02-01 12:09:57 +08:00 via iPhone
    卡顿是因为 apply_async 返回 AsyncResult 对象,但是运行结果实际在 join()时才执行得到。AsyncResult 可以手动 get()结果,也可以加上 callback 参数来指定回调动作。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2773 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 13:44 · PVG 21:44 · LAX 05:44 · JFK 08:44
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.