一个使用gevent协程池的问题代码分析

主代码:

from multiprocessing import Pool
import gevent
from gevent import monkey; monkey.patch_all()
import requests

def test1():
    for i in range(10000):
        rep=requests.get("http://www.163.com/")
    print "test1",req.status_code

def test2():
    for i in range(10000):
        rep=requests.get("http://www.163.com/")
        print "test2",req.status_code

def coroutine():
    gevent.joinall([
        gevent.spawn(test1),
        gevent.spawn(test2)
    ])
if __name__=="__main__":
    p=Pool()
    for i in range(4):
        p.apply_async(coroutine,args=())
    p.close()
    p.join()


strace的结果:

open("/dev/shm/9VRonB", O_RDWR|O_CREAT|O_EXCL, 0600) = 8
write(8, "\1\0\0\0\0\0\0\0\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 32) = 32
mmap(NULL, 32, PROT_READ|PROT_WRITE, MAP_SHARED, 8, 0) = 0x7f7c82fc7000
link("/dev/shm/9VRonB", "/dev/shm/sem.mp29419-10437161768614121538") = 0
fstat(8, {st_mode=S_IFREG|0600, st_size=32, ...}) = 0
unlink("/dev/shm/9VRonB")               = 0
close(8)                                = 0
unlink("/dev/shm/sem.mp29419-10437161768614121538") = 0
open("/sys/devices/system/cpu/online", O_RDONLY|O_CLOEXEC) = 8
read(8, "0-3\n", 8192)                  = 4
close(8)                                = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f7c82fac9d0) = 29428
rt_sigaction(SIGCHLD, {0x7f7c8138da20, ~[KILL STOP RTMIN RT_1], SA_RESTORER|SA_RESTART, 0x7f7c82b9d390}, NULL, 8) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f7c82fac9d0) = 29429
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f7c82fac9d0) = 29430
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f7c82fac9d0) = 29431
clock_gettime(CLOCK_MONOTONIC, {10471, 134885769}) = 0
read(6, 

fork 之后,一直阻塞在read6

然后,ps查看进程,所有进程都是处于可中断睡眠状态

wan       9710  0.0  0.0  15964  1076 pts/2    S+   12:30   0:00 grep --color=auto python
root     22237  0.0  3.0 363416 115696 ?       Sl   10:01   0:02 /usr/bin/python /usr/bin/ubuntu-kylin-software-center-daemon
wan      22325  0.0  0.9 555788 37472 ?        Sl   10:01   0:00 python indicator-china-weather.py
root     28661  0.0  0.5  63904 19828 pts/8    S    11:22   0:00 python ceshi.py
root     28668  0.0  0.0      0     0 pts/8    Z    11:22   0:00 [python] <defunct>
root     28669  0.0  0.0      0     0 pts/8    Z    11:22   0:00 [python] <defunct>
root     28670  0.0  0.0      0     0 pts/8    Z    11:22   0:00 [python] <defunct>
root     28671  0.0  0.0      0     0 pts/8    Z    11:22   0:00 [python] <defunct>
root     29417  0.0  0.0   5208  1284 pts/8    S+   11:26   0:00 strace python ceshi.py
root     29419  0.0  0.5  63648 19856 pts/8    S+   11:26   0:00 python ceshi.py
root     29428  0.0  0.4  63904 15840 pts/8    S+   11:26   0:00 python ceshi.py
root     29429  0.0  0.4  63904 15840 pts/8    S+   11:26   0:00 python ceshi.py
root     29430  0.0  0.4  63904 15748 pts/8    S+   11:26   0:00 python ceshi.py
root     29431  0.0  0.4  63904 15760 pts/8    S+   11:26   0:00 python ceshi.py

接着是查看管道fd 6和对应的文件持有:

root@wan-ThinkPad-E450c:/proc/28661/fd# ls -l
total 0
lrwx------ 1 root root 64 Jul 25 11:29 0 -> /dev/pts/8
lrwx------ 1 root root 64 Jul 25 11:29 1 -> /dev/pts/8
lrwx------ 1 root root 64 Jul 25 11:29 11 -> anon_inode:[eventpoll]
lr-x------ 1 root root 64 Jul 25 11:29 12 -> pipe:[245222]
l-wx------ 1 root root 64 Jul 25 11:29 13 -> pipe:[245222]
lrwx------ 1 root root 64 Jul 25 11:28 2 -> /dev/pts/8
lr-x------ 1 root root 64 Jul 25 11:29 3 -> pipe:[246153]
l-wx------ 1 root root 64 Jul 25 11:29 4 -> pipe:[246153]
lr-x------ 1 root root 64 Jul 25 11:29 5 -> /dev/urandom
lr-x------ 1 root root 64 Jul 25 11:29 6 -> pipe:[246154]
l-wx------ 1 root root 64 Jul 25 11:29 7 -> pipe:[246154]
root@wan-ThinkPad-E450c:/proc/28661/fd# lsof |grep 246154
lsof: WARNING: can't stat() fuse.gvfsd-fuse file system /run/user/1000/gvfs
      Output information may be incomplete.
python    28661                   root    6r     FIFO               0,10        0t0     246154 pipe
python    28661                   root    7w     FIFO               0,10        0t0     246154 pipe

发现整个程序是阻塞在管道上。整个代码那个地方用到了管道?就是multiprocess pool 用了quque, 有个simplequeue ,里面用了管道,这里的管道是否是被patch掉了?查看patch 模块,发现patch_threading模块有下面描述:

def patch_thread(threading=True, _threading_local=True, Event=False, logging=True,
                 existing_locks=True,
                 _warnings=None):
    """
    Replace the standard :mod:`thread` module to make it greenlet-based.
    - If *threading* is true (the default), also patch ``threading``.
    - If *_threading_local* is true (the default), also patch ``_threading_local.local``.
    - If *logging* is True (the default), also patch locks taken if the logging module has
      been configured.
    - If *existing_locks* is True (the default), and the process is still single threaded,
      make sure than any :class:`threading.RLock` (and, under Python 3, :class:`importlib._bootstrap._ModuleLock`)
      instances that are currently locked can be properly unlocked.
    .. caution::
        Monkey-patching :mod:`thread` and using
        :class:`multiprocessing.Queue` or
        :class:`concurrent.futures.ProcessPoolExecutor` (which uses a
        ``Queue``) will hang the process.
    .. versionchanged:: 1.1b1
        Add *logging* and *existing_locks* params.
    """
    # XXX: Simplify
    # pylint:disable=too-many-branches,too-many-locals

    # Description of the hang:
    # There is an incompatibility with patching 'thread' and the 'multiprocessing' module:
    # The problem is that multiprocessing.queues.Queue uses a half-duplex multiprocessing.Pipe,
    # which is implemented with os.pipe() and _multiprocessing.Connection. os.pipe isn't patched
    # by gevent, as it returns just a fileno. _multiprocessing.Connection is an internal implementation
    # class implemented in C, which exposes a 'poll(timeout)' method; under the covers, this issues a
    # (blocking) select() call: hence the need for a real thread. Except for that method, we could
    # almost replace Connection with gevent.fileobject.SocketAdapter, plus a trivial
    # patch to os.pipe (below). Sigh, so close. (With a little work, we could replicate that method)

    # import os
    # import fcntl
    # os_pipe = os.pipe
    # def _pipe():
    #   r, w = os_pipe()
    #   fcntl.fcntl(r, fcntl.F_SETFL, os.O_NONBLOCK)
    #   fcntl.fcntl(w, fcntl.F_SETFL, os.O_NONBLOCK)
    #   return r, w
    # os.pipe = _pipe

    # The 'threading' module copies some attributes from the
    # thread module the first time it is imported. If we patch 'thread'
    # before that happens, then we store the wrong values in 'saved',
    # So if we're going to patch threading, we either need to import it
    # before we patch thread, or manually clean up the attributes that
    # are in trouble. The latter is tricky because of the different names
    # on different versions.

   取消掉patch_threading后,恢复正常。 

   其中这里谈谈multiprocess和threading的Queue共享方式, 这里差别比较大,线程模块使用的mmap,而进程模块使用的是管道,lock和信号量, 管道缓存, lcok保证一致性, 信号量事件通知。 进程队列的消耗远大于线程同步,所以,也是数据库连接池不用进程共享的一个原因。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值