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
tomzhu
V2EX  ›  Python

怎样异步执行 gevent.spawn(),避免主进程阻塞?

  •  
  •   tomzhu · 2016-10-20 17:16:54 +08:00 · 11990 次点击
    这是一个创建于 2958 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一直在用 multiprocessing 和 threading ,遇到内存、资源消耗等问题,想要用gevent.spawn()改写。

    我的主进程是一个 rpc 服务器(不能被阻塞),我只想要异步执行一段代码,不想要结果(代码片段会调用 client 通知结果给主进程), 如果用join 会阻塞。

    看了一篇文章 http://www.jianshu.com/p/571db0eb95f8 作者说道:

    我在 Python shell 中 gevent.spawn 一个函数,它怎么不运行? 按照自然的思维就是 spawn 后就应该要运行啊。 Erlang 就是这样的

    为什么 spawn 出的协程要 join ,一调用 join 就会把整个 python process 阻塞住。丝毫感觉不到异步啊。

    在学习两次失败后,后来终于想明白了:

    shell 中 spawn 不运行,是因为 gevent 的 event loop 没有跑起来,无法去调度 greenlet 。

    join 就是在启动 gevent 的 gevent loop ,一旦 gevent loop 通过其他方式启动起来了, 那么就可以在程序中自然的 spawn 进程。新 spawn 的 greenlet 会被调度执行。

    当跨过这个障碍后,学习和使用 Gevent 容易了很多。

    很想知道怎样启动 gevent loop ,可以让我新的 spawn 自动异步运行? 尝试了以下代码

    gevent.spawn(task)
    gevent.sleep(0)
    

    可以跑通,但是 task 函数内如果有 gevent.sleep(2), grequest 之类的语句,就会出现跑一半不跑了的情况,觉得不靠谱会这样那样的意外,因为我没有办法干涉 task 函数。

    这让我感觉到 gevent 在 python 大型应用程序的使用率应该很低,要么全部都用,要么全部都不能用,大部分团队多半会选择后者。

    想的有点多了,不知道是不是这样呢

    17 条回复    2016-10-24 15:41:55 +08:00
    GeekGao
        1
    GeekGao  
       2016-10-20 17:20:00 +08:00
    “是因为 gevent 的 event loop 没有跑起来,无法去调度 greenlet ” 你确定是这个原因? spawn 一个函数,函数里内容是啥?
    tomzhu
        2
    tomzhu  
    OP
       2016-10-20 17:32:45 +08:00
    @GeekGao 所以我必须要知道函数的内容才能用 gevent.spawn 吗? FYI, 函数内容是调用 client 给 rpc 服务器发送请求并获取返回,这个请求可能瞬间也可能需要数秒,取决于服务器怎么处理。
    yufpga
        3
    yufpga  
       2016-10-20 17:54:23 +08:00
    先找到程序中被阻塞部分, 改为非阻塞的.
    tomzhu
        4
    tomzhu  
    OP
       2016-10-20 17:59:46 +08:00
    @yufpga 问:怎么改?答:用 gevent.spawn 。"又回到最初的起点,呆呆的站在镜子前,笨拙#~*&(@$!#$^@#%!~"
    neoblackcap
        5
    neoblackcap  
       2016-10-20 18:06:53 +08:00
    @tomzhu 你需要了解 Linux 编程里面的 IO 操作, gevent.spawn 只是一个封装,你大可用系统调用 ioctl 将 fd 设成非堵塞,那么所有的读都是非堵塞的了
    yufpga
        6
    yufpga  
       2016-10-20 18:14:35 +08:00
    我不知道你的程序阻塞在哪了, 建议先看一下 gevent 的猴子补丁部分的内容(会让你对这个问题出现的原因有个大概的认知), 如果猴子补丁并不能解决你阻塞的问题, 建议还是换一个方案.当然, 阻塞变非阻塞不是 gevent.spawn 干的事.
    GeekGao
        7
    GeekGao  
       2016-10-20 18:16:42 +08:00
    @tomzhu 一次 spawn 看不出啥的
    fds
        8
    fds  
       2016-10-20 18:21:22 +08:00
    好几年没用了。不过我印象中 gevent 是协作式的并行,同一时刻只能有一个协程运行,必须由它主动让出自己的执行状态,换成别的协程。
    所以如果某一协程要做网络调用,那么应该在发起非阻塞请求,然后让出执行权,等待别人再把执行权交给回自己。
    一般都是通过 monkey patch ,把 python 的一些库函数包装成使用 gevent 的。但有些没包装的,就可能会阻塞了,要自己改。
    binux
        9
    binux  
       2016-10-20 18:29:08 +08:00
    你是不是理解错了什么, gevent 并不能让你的程序「异步」运行,它只能帮你在 event wait 时切换协程罢了。
    例如你的 task 是计算型的,没有 event wait 或者主线程不将执行权限交还给 gevent (例如 gevent.sleep ),它怎么切换?怎么去执行其他的协程?
    binux
        10
    binux  
       2016-10-20 18:29:50 +08:00
    所以,该用进程或者线程的时候就要用啊。
    neoblackcap
        11
    neoblackcap  
       2016-10-20 18:38:39 +08:00
    @binux 我觉得,还是先推荐读 UNP 比什么都重要,读后才开始“异步”编程,否则一个 sleep ,或者 gevent 里面写日志到本地文件,就全线爆炸了。
    太多新人根本不了解所谓的异步,同步,堵塞,非堵塞然后就开始上各种框架,这是一切的问题的根源啊。
    tomzhu
        12
    tomzhu  
    OP
       2016-10-20 19:02:49 +08:00
    看了这么多回复,原来 gevent 并不能让我异步化,我还是继续用 threading 吧,谢谢大家!
    tomzhu
        13
    tomzhu  
    OP
       2016-10-20 19:06:11 +08:00
    @yufpga 我的程序阻塞在 task 函数里面。猴子补丁会让我的应用程序充满不确定性,除非我很清楚他究竟 monkeypatch 了哪些模块,否则我不会用的
    tomzhu
        14
    tomzhu  
    OP
       2016-10-20 19:12:23 +08:00
    @binux 嗯我想我在那个地方应该用的是线程,谢谢你对 gevent ,和切换协程的解释
    neoblackcap
        15
    neoblackcap  
       2016-10-20 19:21:16 +08:00
    @tomzhu 不用 monkeypatch.patch_all(),你可以自己一个一个 patch ,具体 patch 了什么 https://github.com/gevent/gevent/blob/master/src/gevent/monkey.py#L575
    两大切记, gevent 在面对 CPU 密集型以及本地 IO 密集型的任务,都很无力
    sleshep
        16
    sleshep  
       2016-10-22 02:17:36 +08:00
    @binux
    我怀疑他的 patch_all 不在第一行。。。。
    tomzhu
        17
    tomzhu  
    OP
       2016-10-24 15:41:55 +08:00
    @sleshep 请先看懂我的问题谢谢!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3015 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 41ms · UTC 14:42 · PVG 22:42 · LAX 06:42 · JFK 09:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.