併發程式設計——定時器,協程,greenlet模組,gevent模組,單執行緒的套接字併發,asyncio模組
阿新 • • 發佈:2020-08-27
一、定時器
Timer的父類是Thread,所以定時器這裡用的是執行緒
# 多長時間之後執行一個任務 from threading import Timer def task(name): print('我是大帥比--%s'%name) if __name__ == '__main__': # t = Timer(2, task,args=('lqz',)) # 本質是開了個執行緒,延遲一秒執行 Timer(秒數,函式,arges=一個元組) t = Timer(2, task,kwargs={'name':'lqz'}) # 本質是開了個執行緒,延遲一秒執行t.start()
二、協程
協程是為了實現單執行緒下的併發,屬性執行緒下
協程要解決的問題:儲存狀態+切換
yield:生成器,只要函式中有yield關鍵字,這個函式就是生成器,通過yield可以實現儲存狀態+切換
import time # 序列執行 def func1(): for i in range(100000000): i += 1 def func2(): for i in range(100000000): i += 1 if __name__ == '__main__': ctime = time.time() func1() func2()print(time.time() - ctime) # 7.03256796435 # 通過yield,實現儲存狀態加切換(自定義的切換,並不是遇到io才切,所有它並不能節約時間) # 單純的切換,不但不會提高效率,反而會講低效率 def func1(): for i in range(100000000): i += 1 yield def func2(): g=func1() # 先執行一下func1 for i in range(100000000): i += 1 next(g) # 回到func1執行 if __name__== '__main__': ctime = time.time() func2() print(time.time() - ctime) #14.764776706695557 # 協程並不是真是存在的某個東西,而是程式設計師臆想出來的 # 程式設計師控制,不讓自己的程式遇到io,看上去,就實現併發了 ''' 優點如下: 協程的切換開銷更小,屬於程式級別的切換,作業系統完全感知不到,因而更加輕量級 單執行緒內就可以實現併發的效果,最大限度地利用cpu
缺點如下: 協程的本質是單執行緒下,無法利用多核,可以是一個程式開啟多個程序,每個程序內開啟多個執行緒,每個執行緒內開啟協程 協程指的是單個執行緒,因而一旦協程出現阻塞,將會阻塞整個執行緒
總結協程特點: 必須在只有一個單執行緒裡實現併發 修改共享資料不需加鎖 使用者程式裡自己儲存多個控制流的上下文棧(需要儲存狀態) 附加:一個協程遇到IO操作自動切換到其它協程(如何實現檢測IO,yield、greenlet都無法實現,就用到了gevent模組(select機制)) '''
三、greenlet模組
協程相關模組
from greenlet import greenlet import time # 遇到io不會切,初級模組,gevent模組基於它寫的,處理io切換 def eat(): print('我吃了一口') time.sleep(1) # p.switch() print('我又吃了一口') # p.switch() def play(): print('我玩了一會') e.switch() print('我又玩了一會') if __name__ == '__main__': e = greenlet(eat) p = greenlet(play) e.switch()
四、gevent模組
協程相關模組
# gevent基於greenlet寫的,實現了遇見io自動切換 import gevent import time def eat(name): print('%s 吃了一口' % name) gevent.sleep(1) # io操作 print('%s 又吃了一口' % name) def play(name): print('%s 玩了一會' % name) gevent.sleep(2) print('%s 又玩了一會' % name) if __name__ == '__main__': ctim = time.time() e = gevent.spawn(eat,'lqz') p = gevent.spawn(play,'lqz') e.join() # 等待e執行完成 p.join() print('主') print(time.time() - ctim) #2.0165154933929443 這個程式執行完成,最少需要多長時間 2s多一點 ctim=time.time() eat('lqz') play('lqz') print(time.time()-ctim) # 3.0190377235412598
gevent模組加上猴子補丁
from gevent import monkey;monkey.patch_all()#這句話必須寫 import gevent import time def eat(name): print('%s 吃了一口' % name) time.sleep(1) # io操作,被猴子補丁替換之後,gevent.sleep() print('%s 又吃了一口' % name) def play(name): print('%s 玩了一會' % name) time.sleep(2) print('%s 又玩了一會' % name) if __name__ == '__main__': ctim = time.time() e = gevent.spawn(eat,'lqz') p = gevent.spawn(play,'lqz') e.join() # 等待e執行完成 p.join() print('主') print(time.time() - ctim) #2.0165154933929443
五、單執行緒的套接字併發
使用gevent實現單執行緒下的套接字併發效果
六、asyncio模組
3.4及其之前版本使用的asyncio模組
import time import asyncio # 把普通函式變成協程函式 # 3.4及其以前這麼寫,3.5開始就棄用了,3.5版本及其之後用了不報錯但會提示已經啟用 @asyncio.coroutine def task(): print('開始了') yield from asyncio.sleep(1) #asyncio.sleep(1)模擬io print('結束了') loop=asyncio.get_event_loop() # 獲取一個時間迴圈物件# # 協程函式加括號,並不會真正的去執行,它需要提交給loop,讓loop迴圈著去執行 # 協程函式列表 ctime=time.time() t=[task(),task()] loop.run_until_complete(asyncio.wait(t)) loop.close() print(time.time()-ctime)
現在使用的asyncio模組用法
import time import asyncio from threading import current_thread # 表示我是協程函式,等同於3.5之前的裝飾器 async def task(): print('開始了') print(current_thread().name) await asyncio.sleep(3) # await等同於原來的yield from print('結束了') async def task2(): print('開始了') print(current_thread().name) await asyncio.sleep(2) print('結束了') loop=asyncio.get_event_loop() ctime=time.time() t=[task(),task2()] loop.run_until_complete(asyncio.wait(t)) loop.close() print(time.time()-ctime)
---40---