python之gevent(1)

    因为python线程的性能问题,在python中使用多线程运行代码经常不能达到预期的效果。而有些时候我们的逻辑中又需要开更高的并发,或者简单的说,就是让我们的代码跑的更快,在同样时间内执行更多的有效逻辑、减少无用的等待。gevent就是一个现在很火、支持也很全面的python第三方协程库。
    gevent是python的一个并发框架,以微线程greenlet为核心,使用了epoll事件监听机制以及诸多其他优化而变得高效。而且其中有个monkey类,将现有基于Python线程直接转化为greenlet(类似于打patch)。在运行时的具体流程大概就是:
    当一个greenlet遇到IO操作时,比如访问网络/睡眠等待,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。同时也因为只有一个线程在执行,会极大的减少上下文切换的成本。

gevent基本使用

# -*- coding: utf-8 -*-

import gevent


def f1():
    for i in range(5):
        print 'run func: f1, index: %s ' % i
        gevent.sleep(0)


def f2():
    for i in range(5):
        print 'run func: f2, index: %s ' % i
        gevent.sleep(0)


t1 = gevent.spawn(f1)
t2 = gevent.spawn(f2)
gevent.joinall([t1, t2])

运行后输出如下图所示:


image.png

    由图中可以看出,f1和f2是交叉打印信息的,因为在代码执行的过程中,我们人为使用gevent.sleep(0)创建了一个阻塞,gevent在运行到这里时就会自动切换函数切换函数。也可以在执行的时候sleep更长时间,可以发现两个函数基本是同时运行然后各自等待。

    在实际运用的过程中,我们如果有需要通过人为sleep来增加时间间隔或者确保部分逻辑安全的时候,此处使用就很方便了。当然,更多时候我们还是在需要进行网络请求的时候使用gevent:

# -*- coding: utf-8 -*-

from gevent import monkey; monkey.patch_all()
import gevent
import requests
from datetime import datetime


def f(url):
    print 'time: %s, GET: %s' % (datetime.now(), url)
    resp = requests.get(url)
    print 'time: %s, %d bytes received from %s.' % (
        datetime.now(), len(resp.text), url)


gevent.joinall([
        gevent.spawn(f, 'https://www.python.org/'),
        gevent.spawn(f, 'https://www.yahoo.com/'),
        gevent.spawn(f, 'https://github.com/'),
])

运行上述代码,结果如下:


image.png

由上图可以看出,程序基本在同一时间触发了对三个网站的请求,然后各自进行,分别结束。也就是当gevent发现阻塞之后,让当前急需执行,然后自动切换到了另外的请求中运行。

加锁

如果需要在使用gevent的时候加锁,也是非常方便的:

# -*- coding: utf-8 -*-

import gevent
from gevent.lock import Semaphore

sem = Semaphore(1)


def f1():
    for i in range(5):
        sem.acquire()
        print 'run f1, this is ', i
        sem.release()
        gevent.sleep(1)


def f2():
    for i in range(5):
        sem.acquire()
        print 'run f2, that is ', i
        sem.release()
        gevent.sleep(0.3)


t1 = gevent.spawn(f1)
t2 = gevent.spawn(f2)
gevent.joinall([t1, t2])

运行结果如下:


image.png

由输出可以发现,程序会同时判断是否在sleep以及是否有锁两种情况,然后执行当前的最有操作。

小结

    gevent的优势不仅仅是在代码中调用方便,厉害的是它拥有的monkey机制。假设你不愿意修改原来已经写好的python代码,但是又想充分利用gevent机制,那么你就可以用monkey来做到这一点。你所要做的就是在文件开头打一个patch,那么它就会自动替换你原来的thread、socket、time、multiprocessing等代码,全部变成gevent框架。这一切都是由gevent自动完成的。注意这个patch是在所有module都import了之后再打,否则没有效果。
    甚至在编写的Web App代码的时候,不需要引入gevent的包,也不需要改任何代码,仅仅在部署的时候,用一个支持gevent的WSGI服务器,就可以获得数倍的性能提升。

本文简单介绍了gevent的使用,下一篇将对gevent的部分源码进行分析。

你可能感兴趣的:(python之gevent(1))