????因為python線程的性能問題,在python中使用多線程運行代碼經常不能達到預期的效果。而有些時候我們的邏輯中又需要開更高的并發(fā),或者簡單的說唆樊,就是讓我們的代碼跑的更快,在同樣時間內執(zhí)行更多的有效邏輯刻蟹、減少無用的等待逗旁。gevent就是一個現在很火、支持也很全面的python第三方協(xié)程庫舆瘪。
????gevent是python的一個并發(fā)框架片效,以微線程greenlet為核心,使用了epoll事件監(jiān)聽機制以及諸多其他優(yōu)化而變得高效英古。而且其中有個monkey類淀衣,將現有基于Python線程直接轉化為greenlet(類似于打patch)。在運行時的具體流程大概就是:
????當一個greenlet遇到IO操作時召调,比如訪問網絡/睡眠等待膨桥,就自動切換到其他的greenlet,等到IO操作完成唠叛,再在適當的時候切換回來繼續(xù)執(zhí)行只嚣。由于IO操作非常耗時,經常使程序處于等待狀態(tài)艺沼,有了gevent為我們自動切換協(xié)程册舞,就保證總有greenlet在運行,而不是等待IO障般。同時也因為只有一個線程在執(zhí)行环础,會極大的減少上下文切換的成本。
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])
運行后輸出如下圖所示:
????由圖中可以看出剩拢,f1和f2是交叉打印信息的,因為在代碼執(zhí)行的過程中饶唤,我們人為使用gevent.sleep(0)創(chuàng)建了一個阻塞徐伐,gevent在運行到這里時就會自動切換函數切換函數。也可以在執(zhí)行的時候sleep更長時間募狂,可以發(fā)現兩個函數基本是同時運行然后各自等待办素。
????在實際運用的過程中,我們如果有需要通過人為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/'),
])
運行上述代碼雷滚,結果如下:
由上圖可以看出需曾,程序基本在同一時間觸發(fā)了對三個網站的請求,然后各自進行,分別結束呆万。也就是當gevent發(fā)現阻塞之后商源,讓當前急需執(zhí)行,然后自動切換到了另外的請求中運行谋减。
加鎖
如果需要在使用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])
運行結果如下:
由輸出可以發(fā)現,程序會同時判斷是否在sleep以及是否有鎖兩種情況出爹,然后執(zhí)行當前的最有操作庄吼。
小結
????gevent的優(yōu)勢不僅僅是在代碼中調用方便,厲害的是它擁有的monkey機制严就。假設你不愿意修改原來已經寫好的python代碼总寻,但是又想充分利用gevent機制,那么你就可以用monkey來做到這一點盈蛮。你所要做的就是在文件開頭打一個patch废菱,那么它就會自動替換你原來的thread、socket抖誉、time殊轴、multiprocessing等代碼,全部變成gevent框架袒炉。這一切都是由gevent自動完成的旁理。注意這個patch是在所有module都import了之后再打,否則沒有效果我磁。
????甚至在編寫的Web App代碼的時候孽文,不需要引入gevent的包,也不需要改任何代碼夺艰,僅僅在部署的時候芋哭,用一個支持gevent的WSGI服務器,就可以獲得數倍的性能提升郁副。
本文簡單介紹了gevent的使用减牺,下一篇將對gevent的部分源碼進行分析。