Gevent 是怎么在单线程中实现并发的呢? - V2EX
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
warcraft1236
V2EX    Python

Gevent 是怎么在单线程中实现并发的呢?

  •  
  •   warcraft1236 2017-02-14 18:15:41 +08:00 5524 次点击
    这是一个创建于 3165 天前的主题,其中的信息可能已经有所发展或是发生改变。

    教程链接: http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001407503089986d175822da68d4d6685fbe849a0e0ca35000

    廖雪峰大大的教程中,有这么一句

    3 个网络操作是并发执行的,而且结束顺序不同,但只有一个线程 

    我不理解,为什么单线程就能够并发 3 个网络请求呢

    29 条回复    2017-02-16 10:09:13 +08:00
    skydiver
        1
    skydiver  
       2017-02-14 18:25:22 +08:00
    不是说了协程了么……
    warcraft1236
        2
    warcraft1236  
    OP
       2017-02-14 18:28:34 +08:00
    @skydiver 就是协程是怎么做到并发的呢?
    asd103
        3
    asd103  
       2017-02-14 18:29:45 +08:00 via Android
    div class="reply_content">异步吧,我也不了解
    neoblackcap
        4
    neoblackcap  
       2017-02-14 18:31:56 +08:00
    @warcraft1236 IO 操作的时候就挂起啊,调度器去调度执行另外一个协程
    innoink
        5
    innoink  
       2017-02-14 18:33:38 +08:00
    你这问题就像单核 cpu 怎么支持多任务一样
    需要调度器
    warcraft1236
        6
    warcraft1236  
    OP
       2017-02-14 18:35:15 +08:00
    @neoblackcap 那像例子中,第一个网络请求,挂起了,执行第二个网络请求,不就等于第一个网络请求没有再运行了吗?怎么会是并发呢?
    neoblackcap
        7
    neoblackcap  
       2017-02-14 18:41:21 +08:00
    @warcraft1236 你应该去了解什么叫并发,什么叫并行
    fds
        8
    fds  
       2017-02-14 18:51:24 +08:00
    @warcraft1236 操作系统会继续处理你的请求
    dwood
        9
    dwood  
       2017-02-14 19:06:21 +08:00 via Android
    因为 io 是异步的
    czheo
        10
    czheo  
       2017-02-14 19:09:45 +08:00   1
    这个问题问题很好。虽然我没有自己实现过,但我理解大概是这样的。
    要理解两个概念: 1. 协程( coroutine ) 2.事件驱动( event-driven )。

    1. coroutine (类似于 python 里面的 yield 语法),是把一个 function 执行过程暂停,将其 stack 状态保存,当你需要的时候恢复 stack 状态,从暂停的地方继续执行。
    通过这样,你可以自由的控制程序的暂停和恢复。在你的例子里面其实是,把每个 function 执行到 io 操作时都暂停,等你需要的时候( io 返回结果的时候)恢复执行。
    但问题是,什么时候你知道 io 返回结果了呢?就需要另外一个机制通知你。

    2. event-driven 就是通知你 io 操作完成的机制。基本想法就是 io 操作发生时,你把 io 的 file descriptor 存到一个 poll 里面,操作系统会监控这个 poll 。当其中有 io 结束的时候,操作系统会给你的程序发一个 signal ( event ),告诉你哪一个 io 结束了。这样你就知道什么时候恢复你的程序执行了。

    当然这么简单说一说,如果不了解操作系统底层的一些知识,感觉还是挺难理解的。
    我猜是这么回事,有不对的请指出。
    Allianzcortex
        11
    Allianzcortex  
       2017-02-14 19:17:07 +08:00 via iPhone
    线程有三个状态:等待 wait ,就绪 ready 和运行 running 。
    gamexg
        12
    gamexg  
       2017-02-14 20:31:58 +08:00 via Android
    很多方案,例如:完成端口
    itfanr
        14
    itfanr  
       2017-02-14 20:38:04 +08:00 via Android
    @czheo 说得好。你得保证执行的任务是可以异步的,不能阻塞,然后就可以随便监听完成事件了。就好比一只手抛多个球,只要球没必要一直用手拿着,就能玩很多。
    yongzhong
        15
    yongzhong  
       2017-02-14 20:39:26 +08:00   2
    13 楼第二个链接贴错了,先看这个[聊聊同步、异步、阻塞与非阻塞]

    http://mp.weixin.qq.com/s?__biz=MzA4MjEyNTA5Mw==&mid=2652563596&idx=1&sn=ea234e176e36775effa9634f105ecf6d&scene=21#wechat_redirect
    wizardoz
        16
    wizardoz  
       2017-02-14 20:39:58 +08:00
    只有一个 cpu ,多线程 /多进程是怎么做到并发执行的呢?
    开个玩笑……

    在多线程的思路中,每个线程处理一个连接,连接没有数据的时候,就阻塞等待,有数据的线程运行。
    在异步的思路中,一个线程同时等待多个连接的数据,哪个连接先来了数据就先处理哪个连接,处理完马上回到阻塞状态。
    系统调用 poll 、 select 、 epoll ( linux ) 等提供一个线程可以同时等待多个连接的机制。
    因为网络编程中,等待网络数据是耗时最多的状态,所以使用异步的方法效率很高。
    nicevar
        17
    nicevar  
       2017-02-14 20:59:04 +08:00
    时间片
    czheo
        18
    czheo  
       2017-02-14 21:50:32 +08:00
    稍微看了下 gevent 的具体实现。基本思路是把 python 标准库里面的所有 io 操作都变成 event driven 。好大的工程。。。

    首先是 monkey.patch_all 实际是把所有的 io 模块都打了补丁,具体可以看 patch_module 那个函数,用 gevent.{os, socket, sys...}自定义的操作替代了原生标准模块的操作。
    https://github.com/gevent/gevent/blob/master/src/gevent/monkey.py#L583
    https://github.com/gevent/gevent/blob/master/src/gevent/monkey.py#L152

    比如 gevent.socket 这个模块其实是覆盖了原生的 socket 模块,关键操作在 socket._wait 这个函数,把 io event 注册到 gevent.hub 里面。
    https://github.com/gevent/gevent/blob/master/src/gevent/_socket3.py#L306
    https://github.com/gevent/gevent/blob/master/src/gevent/_socket3.py#L157

    gevent.hub 是对 eventloop 的实现。
    https://github.com/gevent/gevent/blob/master/src/gevent/hub.py
    quxw
        19
    quxw  
       2017-02-14 22:02:52 +08:00
    我刚开始也有这个困惑
    https://pymotw.com/2/select/
    PythonAnswer
        20
    PythonAnswer  
       2017-02-14 22:41:09 +08:00
    cpu 计算协程无效。跑满 100%就只能干瞪眼了。

    协程完成 io 调度后, cpu 还可以干别的。(让学霸同学帮你写作业,你自己可以去打 dota 这种需要 cpu 的劳动,学霸同学写完作业向你报告,你给他 100 块让他滚蛋,顺便打电话喊女朋友送饭给你吃)
    araraloren
        21
    araraloren  
       2017-02-15 08:25:56 +08:00
    那不知道楼主是否知道并发的意思呢,如果明白楼上的各位回答很清楚了。。
    whx20202
        22
    whx20202  
       2017-02-15 10:00:57 +08:00
    warcraft1236
        23
    warcraft1236  
    OP
       2017-02-15 10:13:44 +08:00
    @yongzhong 感谢
    hfpeng01
        24
    hfpeng01  
       2017-02-15 11:33:07 +08:00
    如果没有任务阻塞,那么在单处理器上使用并发就没有任何意义。有阻塞的话,就是切分 CPU 时间片, cpu 轮流给每个任务分配占用时间。
    WangYanjie
        25
    WangYanjie  
       2017-02-15 12:21:44 +08:00
    msg7086
        26
    msg7086  
       2017-02-15 14:55:52 +08:00
    非阻塞就可以实现单个线程并发了。
    软件的线程本身就是非阻塞的,所以一个 CPU 线程可以运行几百几千个软件线程。
    同理如果你操作也是非阻塞的,那一个软件线程也就可以运行几百几千个操作了。
    wwqgtxx
        27
    wwqgtxx  
       2017-02-15 16:19:37 +08:00
    @WangYanjie 现在应该是 libev 了,从 gevent1.0 开始就不用 libevent 了
    itfanr
        28
    itfanr  
       2017-02-15 18:59:10 +08:00 via Android
    @yongzhong 哈哈
    WangYanjie
        29
    WangYanjie  
       2017-02-16 10:09:13 +08:00
    @wwqgtxx 嗯,没注意,提留在 0.9.x 太久了。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3773 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 10:19 PVG 18:19 LAX 03:19 JFK 06:19
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86