單線程下實現併發git
纖程,微線程
非搶佔多任務子程序的計算機組件
容許不一樣入口點的暫停和開始
簡單來講: 協程是能夠暫停執行的函數
記錄一個函數的上下文棧幀(空間),
協程調度切換時會將記錄的上下文保存,在切換回來時候進行調取,
恢復原有的執行內容,以便從上一次執行的位置繼續執行。
優勢:
切換在應用層完成,開銷小
單線程程序,不須要進行共享資源的互斥處理
缺點:
沒法利用計算機多核資源
import time def wrapper(func): def inner(*args,**kwargs): ret =func(*args,**kwargs) next(ret) return ret return inner @wrapper def consumer(): while True: x= yield print(x) def producter(target): '''生產者造值''' # next(g) #至關於g.send(None) for i in range(10): time.sleep(0.5) target.send(i)#要用send就得用兩個yield producter(consumer())
pip3 install greenlet
from greenlet import greenlet import time def eat(name): print('%s eat 1' %name) time.sleep(10) #當遇到IO的時候它也沒有切,這就得用gevent了 g2.switch('egon') print('%s eat 2' %name) g2.switch() def play(name): print('%s play 1' %name) g1.switch() print('%s play 2' %name) g1=greenlet(eat) g2=greenlet(play) g1.switch('egon')#能夠在第一次switch時傳入參數,之後都不須要
對程序內部協做式地調度,輕鬆實現併發同步或異步編程github
自動識別 阻塞 ,而後自行切換任務。不在須要手動編程
pip3 install gevent
g1=gevent.spawn(func,1,2,3,x=4,y=5) # 建立一個協程對象g1,spawn括號內第一個參數是函數名,後面能夠多個參數,能夠是位置實參或關鍵字實參,都是傳給函數eat的 g=gevent.spawn(func2) g1.join() #等待g1結束 g2.join() #等待g2結束 #或者上述兩步合做一步:gevent.joinall([g1,g2]) gevent.sleep(2) # 模擬的是gevent能夠識別的io阻塞 g1.value #拿到func1的返回值 # 當不使用 gevent.sleep(2),使用 time.sleep() 得時候須要導入這個 from gevent import monkey;monkey.patch_all() # 若是不導入直接使用 time.sleep() 會沒法實現單線程併發 # 必須放到被打補丁者的前面,如time,socket模塊以前
基本使用實例服務器
from gevent import monkey;monkey.patch_all() import gevent import time def eat(name): print('%s eat 1' %name) time.sleep(2) print('%s eat 2' %name) return 'eat' def play(name): print('%s play 1' %name) time.sleep(3) print('%s play 2' %name) return 'play' #當有返回值的時候,gevent模塊也提供了返回結果的操做 start = time.time() g1 = gevent.spawn(eat,'egon') #執行任務 g2 = gevent.spawn(play,'egon') #g1和g2的參數能夠不同 # g1.join() #等待g1 # g2.join() #等待g2 #上面等待的兩句也能夠這樣寫 gevent.joinall([g1,g2]) print('主',time.time()-start) #3.001171588897705 print(g1.value) print(g2.value) """ egon eat 1 egon play 1 egon eat 2 egon play 2 主 3.019590377807617 eat play """
爬蟲實例併發
from gevent import monkey;monkey.patch_all() #打補丁 import gevent import requests import time def get_page(url): print('get :%s'%url) response = requests.get(url) if response.status_code==200: #下載成功的狀態 print('%d bytes received from:%s'%(len(response.text),url)) start=time.time() gevent.joinall([ gevent.spawn(get_page,'http://www.baidu.com'), gevent.spawn(get_page, 'https://www.yahoo.com/'), gevent.spawn(get_page, 'https://github.com/'), ]) stop = time.time() print('run time is %s' %(stop-start))
待回調函數得爬蟲實例app
from gevent import joinall, spawn, monkey monkey.patch_all() import requests from threading import current_thread def parse_page(res): print('%s PARSE %s' % (current_thread().getName(), len(res))) def get_page(url, callback=parse_page): print('%s GET %s' % (current_thread().getName(), url)) response = requests.get(url) if response.status_code == 200: callback(response.text) if __name__ == '__main__': urls = [ 'https://www.baidu.com', 'https://www.taobao.com', 'https://www.openstack.org', ] tasks = [] for url in urls: tasks.append(spawn(get_page, url)) joinall(tasks) """ DummyThread-1 GET https://www.baidu.com DummyThread-2 GET https://www.taobao.com DummyThread-3 GET https://www.openstack.org DummyThread-1 PARSE 2443 DummyThread-2 PARSE 141762 DummyThread-3 PARSE 65694 """
gevent 實現併發 socket 異步
import gevent from gevent import monkey monkey.patch_all() from socket import * def server(): s = socket() s.bind(("127.0.0.1", 8090)) s.listen() while True: c, addr = s.accept() # 要在這裏阻塞一下等待鏈接 print("Connect from", addr) # handle(c) # 處理客戶端請求 gevent.spawn(handle,c) # 利用協程來實現高併發 def handle(c): while True: data = c.recv(1024) if not data: break print(data.decode()) c.send(b'ok') c.close() server()
import socket sk = socket.socket() # 建立客戶套接字 sk.connect(('127.0.0.1',8090)) # 嘗試鏈接服務器 sk.send(b'hello!') ret = sk.recv(1024) # 對話(發送/接收) print(ret) sk.close() # 關閉客戶套接字