協程
一、協程的本質:
單線程實作并發,在應用程式裡控制多個任務的切換+儲存狀态
二、協程的目的:
- 想要在單線程下實作并發
- 并發指的是多個任務看起來是同時運作的
- 并發=切換+儲存狀态
三、補充:
- yiled可以儲存狀态,yield的狀态儲存與作業系統的儲存線程狀态很像,但是yield是代碼級别控制的,更輕量級
- send可以把一個函數的結果傳給另外一個函數,以此實作單線程内程式之間的切換
- 如何實作檢測IO,yield、greenlet都無法實作,就用到了gevent子產品(select機制)
四、優點
- 應用程式級别速度要遠遠高于作業系統的切換
五、缺點
- 多個任務一旦有一個阻塞沒有切,整個線程都阻塞在原地,該線程内的其他的任務都不能執行了
- 一旦引入協程,就需要檢測單線程下所有的IO行為,實作遇到IO就切換,少一個都不行,因為如果一個任務阻塞了,整個線程就阻塞了,其他的任務即便是可以計算,但是也無法運作了
注意:單純地切換反而會降低運作效率
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | |
greenlet
greenlet隻是提供了一種比generator更加便捷的切換方式,當切到一個任務執行時如果遇到io,那就原地阻塞,仍然是沒有解決遇到IO自動切換來提升效率的問題。
注意:單純的切換(在沒有io的情況下或者沒有重複開辟記憶體空間的操作),反而會降低程式的執行速度
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |
gevent
遇到IO阻塞時會自動切換任務
一、用法:
- g1=gevent.spawn(func,1,,2,3,x=4,y=5)建立一個協程對象g1,spawn括号内第一個參數是函數名,如eat,後面可以有多個參數,可以是位置實參或關鍵字實參,都是傳給函數eat的
- g2=gevent.spawn(func2)
- g1.join() #等待g1結束
- g2.join() #等待g2結束
- 或者上述兩步合作一步:gevent.joinall([g1,g2])
- g1.value#拿到func1的傳回值
二、補充:
- gevent.sleep(2)模拟的是gevent可以識别的io阻塞,
- 而time.sleep(2)或其他的阻塞,gevent是不能直接識别的需要用下面一行代碼,打更新檔,就可以識别了
- from gevent import monkey;monkey.patch_all()必須放到被打更新檔者的前面,如time,socket子產品之前或者我們幹脆記憶成:要用gevent,需要将from gevent import monkey;monkey.patch_all()放到檔案的開頭
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | |
三、通過gevent實作單線程下的socket并發
from gevent import monkey;monkey.patch_all()一定要放到導入socket子產品之前,否則gevent無法識别socket的阻塞
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | |
"""
網絡程式設計
B/S和C/S架構
osi五層協定
應用層
傳輸層 端口/服務相關 四次路由器 四層交換機
網絡層 ip 路由器 三層交換機
資料鍊路層 arp mac相關 網卡 交換機
實體層
socket/socketserver
tcp/udp
tcp協定:面向連接配接的,可靠的,全雙工的,流失傳輸協定
三次握手
四次揮手
粘包現象
在接受度粘 發送資訊無邊界,在接受端沒有及時接受,在緩存端粘在一起
在發送端粘,時間短,由于優化機制粘在一起的
udp協定:面向資料報的,不可靠 無連接配接的協定
ip
arp
I/O操作 輸入會讓輸出
input輸入到記憶體 input read recv recvfrom accept connect close
output 從記憶體輸出 print write send sendto accept connect close
阻塞和非阻塞
阻塞: cpu不工作 accept recv connect等待
非阻塞:cpu工作
##############################
人機沖突
cpu使用率低
錄音帶存儲+批處理
降低資料的讀取時間
提高cpu的使用率
多道作業系統------在一個任務遇到io的時候主動讓出cpu
資料隔離
時空複用
能夠在一個任務遇到io操作的時候主動把cpu讓出來,給其他的任務使用
切換:占時間,
切換:作業系統做的
分時作業系統------給時間分片,讓多個任務輪流使用cpu
cpu的輪轉
每一個程式配置設定一個時間片
切換:占時間
反而降低來cpu的使用率
提高了使用者體驗
分時作業系統+多道作業系統+實時作業系統
多個程式一起在計算機中執行
一個程式如果遇到io操作,切出去讓出cpu
一個程式沒有遇到io,但是時間片到時了,切出去讓出cpu
網絡作業系統
分布式作業系統
##############################
程序:運作中的程式
程式和程序之間的差別
程式隻是一個檔案
程序是這個檔案被cpu運作起來了
程序是計算機中最小的資源配置設定機關
每一個程序都有一個在作業系統中的唯一辨別符:pid
作業系統排程程序的算法
短作業優先
先來先服務
時間片輪轉
多級回報算法(少在程式的一開始就使用io)
作業系統負責什麼?
排程程序先後執行的順序,控制執行的時間等等
資源的配置設定
并行與并發
并行:兩個程式兩個cpu,每個程式分别占用一個cpu自己執行自己的
看起來是同時執行,實際在每一個時間點上都在各自執行着
并發:兩個程式一個cpu每個程式交替在一個cpu上執行,看起來在同時執行,但是實際上仍然是串行
同步:
調用這個函數,并等待這個函數結束
異步:
調用一個函數,不等待這個方法結束,也不關心這個方法做了什麼
阻塞:cpu不工作
非阻塞:cpu工作
同步阻塞:
conn.recv
調用函數的這個過程,有io操作
同步非阻塞:
func() 沒有io操作
socket 非阻塞的tcp協定的時候
調用函數(這個函數内部不存在io操作)
異步非阻塞:
把func仍到其他任務裡去執行裡,沒有io操作
本身的任務和func任務各自執行各自的,沒有io操作
異步阻塞:
輕按兩下圖示發生的事情:
程式在開始運作之後,并不是立即開始執行代碼,而是會進入就緒狀态,等待作業系統排程開始運作
作業系統為其建立程序,配置設定一塊空間,pid---》就緒(ready)---》運作(runing)---》阻塞(blocking)
運作(沒有遇到io操作時間片到了)---》就緒
運作(遇到io)--》阻塞
運作--》運作完了--》結束程序
阻塞io結束---》就緒
阻塞影響了程式運作的效率
##############################
程序是計算機中最小的資源配置設定機關(程序負責圈資源)
資料隔離的
建立程序,時間開銷大
銷毀程序,時間開銷大
程序之間切換,時間開銷大
線程:是計算機中能被cpu排程的最小機關(線程是負責執行具體代碼的)
線程的建立,也需要一些開銷(一個存儲局部變量的結構,記錄狀态)
建立,銷毀,切換開銷遠遠小于程序
每個程序中至少有一個線程
一個程序中的多個線程是可以共享這個程序的資料的
是程序中的一部分
每一個程序中至少有一個線程,線程是負責執行具體代碼的
隻負責執行代碼,不負責存儲共享的資料,也不負責資源配置設定
python中的線程比較特殊,是以程序也有可能被用到。
pid子程序
ppid父程序
在pycharm中啟動的所有py程式都是pycharm的子程序
tcp: 可靠
https: 安全
單核 并發
多核 多個程式跑在多個cpu上,在同一時刻并行
##############################
作業系統
1。計算機中所有的資源都是由作業系統配置設定的
2。作業系統排程任務:時間分片,多道機制
3。cpu的使用率是我們努力的名額
程序:開銷大 資料隔離 資源配置設定機關 cpython下可以利用多核 由作業系統排程的
程序的三狀态:就緒 運作 阻塞
multiprocessing子產品
Process-開啟程序
Lock-互斥鎖
為什麼要在程序中加鎖
因為程序操作檔案也會發生資料不安全
Queue -隊列 IPC機制(Pipe,redis,memcache,rabbitmq,kafka)
Manager-提供資料共享機制
線程:開銷小 資料共享 cpu排程機關 cpython下不能利用多核 由作業系統排程的
GIL鎖:全局解釋器鎖,Cpython解釋器提供的,緻了一個程序中多個線程同一時刻隻有一個線程能通路CPU--多線程不能利用多核
Thread類--能開啟線程start,等待線程結束join
Lock-互斥鎖 互斥鎖能在一個線程中連續acquire,效率相對高
Rlock--遞歸鎖 可以在一個線程中連續acquire,效率相對低
死鎖現象如何發生,如何避免?
線程隊列queue子產品
Queue
LifoQueue
PriorityQueue
池
執行個體化一個池
送出任務到池中,傳回一個對象
使用這個對象擷取傳回值
回調函數
阻塞等待池中的任務都結束
資料安全問題
資料隔離和通信
協程:多個任務在一條線程上來回切換,使用者級别的,由我們自己寫的python代碼來控制切換的,是作業系統不可見的
在Cpython解釋器下----協程和線程都不能利用多核,都是在一個CPU上輪流切換
由于多線程本身就不能利用多核
是以即便是開啟來多個線程也隻能輪流在一個cpu上執行
協程如果把所有任務的IO操作都規避掉,隻剩下需要使用CPU的操作
就意味着協程就可以做到提高CPU使用率的效果
多線程和協程
線程 切換需要作業系統,開銷大 給作業系統的壓力大
作業系統對IO操作的感覺更加靈敏
協程 切換需要 python代碼 開銷小,使用者操作可控 完全不會增加作業系統的壓力
使用者級别能夠對IO操作的感覺比較低
協程:能夠在一個線程下的多個任務之間來回切換,那麼每一個任務都是一個協程
兩種切換方式
原生python完成 yield asyncio
c語言完成的python子產品 greenlet gevent
1。一個線程中的阻塞都被其他的各種任務占滿了
2。讓作業系統覺得這個線程很忙
盡量的減少這個線程進入阻塞的狀态
提高了單線程對CPU的使用率
3。多個任務在同一個線程中執行
也達到了一個并發的效果
規避了每一個任務的io操作
減少了線程的個數,減輕了作業系統的負擔
我們寫協程:在一條線程上最大限度的提高CPU的使用率
在一個任務中遇到IO的時候就切換到其他任務
特點:
開銷很小,是使用者級的,隻能從使用者級别感覺的IO操作
不能利用多核,資料共享,資料安全
子產品和用法:
gevent (擴充子產品)基于greenlet(内置子產品)切換 aiohttp爬蟲子產品基于asyncio sanic異步架構
先導入子產品
導入monkey,執行patch all
寫一個函數當作協程要執行的任務
協程對象 = gevent.spawn(函數名,參數,)
協程對象.join(),gevent.joinall([g1,g2...])
分辨gevent是否識别了我們寫的代碼中的io操作的方法
在patchall之前列印一下涉及到io操作的函數位址
在patchall之後列印一下涉及到io操作的函數位址
如果兩個位址一緻,說明gevent沒有識别這個io,如果不一緻說明識别了
asyncio 基于yield機制切換的
async 辨別一個協程函數
await 後面跟着一個asyncio子產品提供的io操作的函數
loop 事件循環,負責在多個任務之間進行切換的
最簡單的協程函數是如何完成的
"""
import os
import time
from multiprocessing import Process
from multiprocessing import Queue
def func(exp,q):
print('start',os.getpid())
ret = eval(exp)
q.put(ret)
time.sleep(1)
print('end',os.getpid())
if __name__ == '__main__': #window下需要加上這一句
q=Queue() #先進先出
p=Process(target=func,args=('1+2+3',q)) #建立一個即将要執行func函數的程序對象
# p.daemon=True #守護程序是随着主程序的代碼的結束而結束的
p.start() #異步非阻塞 調用開啟程序的方法,但是并不等待這個程序真的開啟
# p.join() #同步阻塞直到p對應的程序結束之後才結束阻塞
# print(p.is_alive())
# p.terminate() # 異步非阻塞 強制結束一個子程序
# print(p.is_alive()) #子程序還活着,因為作業系統還沒來得及關閉程序
# time.sleep(0.01)
# print(p.is_alive())#作業系統已經響應了我們要關閉程序的需求,再去檢測的時候,得到的結束是程序已經結束了
print('main',os.getpid()) #主程序代碼執行完畢,但是主程序沒有結束,等待子程序結束
print(q.get())
"""
作業系統建立程序的方式不同
Windows作業系統執行開啟程序的代碼
實際上新的子程序需要通過import父程序的代碼來完成資料的導入工作
是以有一些内容我們隻希望在父程序中完成,就寫在if __name__ == '__main__': 下面
iOS和Linux是直接從記憶體級别去拷貝代碼(fork),從父程序拷貝資料到子程序
主程序沒有結束,等待子程序結束
主程序負責回收子程序的資源
如果子程序執行結束,父程序沒有回收資源,那麼這個子程序會變成一個僵屍程序
主程序的結束邏輯:
主程序的代碼結束
所有的子程序結束
給子程序回收資源
主程序結束
主程序怎麼知道子程序結束的呢? 監控檔案
基于網絡,檔案
join方法
把一個程序的結束事件封裝成一個join方法
執行join方法的效果就是阻塞直到這個子程序執行結束就結束阻塞
在多個子程序中執行join
p=Process(target=函數名,args=(參數,))
程序對象和程序并沒有直接的關系,隻是存儲來一些和程序相關的内容,此時此刻,作業系統還沒有接到建立程序的指令
p.start()開啟來一個程序----這個方法相當于給來作業系統一條指令
satrt方法的非阻塞和異步的特點
在執行開啟程序這個方法的時候
我們既不等待這個程序開啟,也不等待作業系統給我們的相應
這裡隻是負責通知作業系統去開啟一個程序
開啟了一個子程序之後,主程序的代碼和子程序的代碼完全異步
p.daemon 守護程序是随着主程序代碼的結束而結束的
所有的子程序都必須在主程序結束之前結束,由主程序來負責回收資源
p.is_alive()
p.terminate() 強制結束一個子程序的
什麼是異步非阻塞?
terminate,start
什麼是同步阻塞
join
1.如果在一個并發的場景下,涉及到某部分内容是需要修改一些所有程序共享的資料資源,需要加鎖來維護資料的安全
2.在資料安全的基礎上,才考慮效率問題
3.同步存在的意義就是資料安全
在主程序中執行個體化 lock=Lock()
在子程序中,對需要加鎖的代碼進行with lock:
with lock: 相當于lock.acquire()和lock.release()
在程序中需要加鎖的場景
1.操作共享的資料資源(檔案,資料庫)
2.對資源進行修改,删除操作
程序之間的通訊--IPC(inter process communication)
隊列和管道都是IPC機制,隊列程序之間資料安全,管道程序之間資料不安全
第三方工具(軟體)提供給我們的IPC機制,叢集的概念(高可用)
redis
memcache
kafka
rabbitmq
并發需求,高可用,斷電儲存資料,解耦
from multiprocessing import Queue
Queue(天生就是資料安全的) 基于 檔案家族的socket,pickle和lock實作的
隊列就是管道加鎖
get():阻塞的
get_nowait():非阻塞的
下面三個方法不推薦使用,多程序中不準确()
q.empty() 判斷隊列是否為空
q.qsize() 傳回隊列中的資料個數
q.full() 判斷隊列是否為滿
pipe 管道(不安全的)基于 檔案家族的socket,pickle實作的
from multiprocessing import Pipe
pip=Pipe()
pip.send()
pip.recv()
解耦:修改 複用 可讀性
把寫在一起的大的功能分開成多個小的功能處理
joinablequeue
q.join() 阻塞 直到這個隊列中所有的内容都被取走且task_done
multiprocessing中有一個manager類
封裝了所有和程序相關的,資料共享,資料傳遞相關的資料類型
但是對于字典清單這一類的資料操作的時候會産生資料不安全
需要加鎖解決問題,并且需要盡量少的使用這種方式
線程本身是可以利用多核的
cpython解釋器中不能實作多線程利用多核
垃圾回收機制 gc 引用計數+分代回收
專門有一條線程來完成垃圾回收
對每一個在程式中的變量統計引用計數
鎖:GIL全局解釋器鎖
cpython解釋器中的機制,導緻了在同一個程序中多個線程不能同時利用多核。
python的多線程隻能是并發不能是并行
線程即便有GIL,也會出現資料不安全的問題
1.操作的是全局變量
2.做以下操作:
+=,-=,*=,/=先計算再指派才容易出現資料不安全的問題
包括list[0]+=1,dic['key']-=1
a=0
def func():
global a
a+=1
import dis
dis.dis(func) #檢視func的cpu指令
互斥鎖是鎖中的一種:在同一個線程中,不能連續acquire多次,(在一個線程中連續多次acquire會死鎖)
死鎖現象:有多把鎖+多把鎖交替使用,互斥鎖在一個線程中連續acquire
遞歸鎖 (在一個線程中連續多次acquire不會死鎖)
好:在同一個程序中多次acquire也不會發生阻塞
不好:占用了更多資源
遞歸鎖---将多把互斥鎖變成了一把遞歸鎖,遞歸鎖也會發生死鎖現象(多把遞歸鎖交替使用的時候),以後的代碼盡量用一把鎖。
mutexB=mutexA=RLock()
注意不是:mutexB=RLock() mutexA=RLock() 不然還是一樣會發生死鎖現象
保證了整個python程式中,隻能有一個線程被CPU執行
原因;cpython解釋器中特殊的垃圾回收機制
GIL鎖導緻來線程不能并行,但是可以并發
是以使用多線程并不影響高io型的操作
隻會對高計算型的程式有效率上的影響
遇到高計算的:多程序+多線程 或者 分布式
遇到IO操作的時候
5億條cpu指令/s
5-6cpu指令==一句python代碼
幾千萬條python代碼
web架構,幾乎都是多線程
主線程什麼時候結束?等待所有子線程結束之後才結束
主線程如果結束了,主程序也就結束了
t.ident 線程id
from threading import Thread,current_thread,active_count,enumerate
active_count()==len(enumerate())
current_thread.ident 線程id 在哪一個線程裡,current_thread得到的就是這個目前線程的資訊
線上程中不能從主線程結束一個子線程 沒有terminate
不要在子線程裡随便修改全局變量
守護線程一直等到所有的非守護線程都結束之後才結束
除了守護了主線程的代碼之外也會守護子線程
p.daemon 守護程序是随着主程序代碼的結束而結束的
非守護線程不結束,主線程也不結束,主線程結束了,主程序也結束
結束順序:
非守護線程結束--》主線程結束--》主程序結束——》守護線程也結束
什麼是生産者消費者模型
把一個産生資料并且處理資料的過程解耦
讓生産資料的過程和處理資料的過程達到一個工作效率上的平衡
中間的容器,在多程序中使用隊列或者可被join的隊列,做到控制資料的量
當資料過剩的時候,隊列的大小會控制消費者的行為
當資料嚴重不足的時候,隊列會控制消費者的行為
并且還可以通過定期檢測隊列中元素的個數來調節生産者消費者的個數
爬蟲:
請求網頁的平均時間是0.3s
處理網頁代碼的時候是0.003s
100倍,每啟動100個線程生産資料,就啟動一個線程來處理資料
web程式的server端
每秒鐘有6w條請求
一個服務每s中隻能處理2000條
先寫一個web程式,隻負責一件事情,就是接收請求,然後把請求放到隊列中(消息中間件)
再寫很多個server端,從隊列中擷取請求,然後處理,然後傳回結果
# from queue import Queue #先進先出隊列
import queue #線程之間的通信,線程安全
先進先出:寫一個server所有的使用者的請求放在隊列裡,先來先服務的思想 q=queue.Queue(3)
後進先出:算法中 q=queue.LifoQueue(3) #後進先出->堆棧
優先級隊列:自動的排序(vip号碼段)告警級别 q=queue.PriorityQueue(3) #優先級隊列 q.put((10,'one'))
池:預先的開啟固定個數的程序數,當任務來臨的時候,直接送出給已經開好的程序/線程,讓其去執行就可以了
節省了程序,線程的開啟 關閉 切換鎖花費的時間
并且減輕了作業系統排程的負擔
shutdown 關閉池之後就不能繼續送出任務,并且會阻塞,直到已經送出的任務完成
result() 同步阻塞
submit() 異步非阻塞
5*20*500
是單獨開啟線程程序還是池?
如果隻是開啟一個子程序做一件事情,就可以單獨開線程
有大量的任務等待程式去做,要達到一定的并發數,開啟線程池
根據你程式的io操作也可以判定是用池還是不用池
socket大量的阻塞io ,socket server就沒有用池,用的是線程
爬蟲的時候,池
回調函數:執行完子線程任務之後直接調用對應的回調函數
爬取網頁 需要等待資料傳輸和網絡上的響應高IO的--子線程
分析網頁,沒有什麼IO操作--這個操作沒必要在子線程完成,交給回調函數
兩件事情:存在IO操作的事情,基本不存在IO操作的事情
obj=tp.submit(io操作對應的函數)
obj.result() 是一個阻塞方法 擷取傳回值
obj.add_done_callback(計算型的事情)
obj=tp.submit(需要在子線程執行的函數名,參數)
obj.add_done_callback(子線程執行完畢之後要執行的代碼對應的函數)
程序和線程都有鎖
所有線上程中能工作的基本都不能在程序中工作
在程序中能夠使用的基本線上程中也可以使用
沒一個程序下面都有一個獨立的垃圾回收機制的線程,不需要擔心開了多程序以後,垃圾回收機制怎麼處理
"""
from multiprocessing import process
from threading import Thread
import os
def tfunc():
print(os.getpid())
def pfunc():
print('pfunc-->',os.getpid())
Thread(target=tfunc).start()
if __name__ == '__main__':
Process(target=pfunc).start()
from multiprocessing import Queue
import queue
q=Queue(3)
q.put(1)
q.put(2)
q.put(3)
print('aaa')
# q.put(44444) #當隊列為滿的時候再向隊列中放資料,隊列會阻塞
try:
q.put_nowait(6) #當隊列為滿的時候再向隊列中放資料,會報錯并且會丢失資料
except queue.Full:
pass
print('bbb')
print(q.get())
print(q.get())
print(q.get())
# print(q.get()) #在隊列為空的時候會發生阻塞
try:
q.get_nowait() #當隊列為空的時候直接報錯
except queue.Empty:
pass
"""
##############################
協程:多個任務在一條線程上來回切換
我們寫協程:在一條線程上最大限度的提高CPU的使用率
在一個任務中遇到IO的時候就切換到其他任務
特點:
開銷很小,是使用者級的,隻能從使用者級别感覺的IO操作
不能利用多核,資料共享,資料安全
子產品和用法:
gevent 基于greenlet切換
先導入子產品
導入monkey,執行patch all
寫一個函數當作協程要執行的任務
協程對象 = gevent.spawn(函數名,參數,)
協程對象.join(),gevent.joinall([g1,g2...])
分辨gevent是否識别了我們寫的代碼中的io操作的方法
在patchall之前列印一下涉及到io操作的函數位址
在patchall之後列印一下涉及到io操作的函數位址
如果兩個位址一緻,說明gevent沒有識别這個io,如果不一緻說明識别了
asyncio 基于yield機制切換的
async 辨別一個協程函數
await 後面跟着一個asyncio子產品提供的io操作的函數
loop 事件循環,負責在多個任務之間進行切換的
最簡單的協程函數是如何完成的
"""
from gevent import monkey
monkey.patch_all()
import gevent
import time
def eat(name):
print('%s eat 1' % name)
time.sleep(3)
print('%s eat 2' % name)
def play(name):
print('%s play 1' % name)
time.sleep(2)
print('%s play 2' % name)
return '傳回值'
g_l=[]
for i in range(10):
g = gevent.spawn(eat, 'tom') #注意要遇到阻塞才會切換
g_l.append(g)
gevent.joinall(g_l) #這就是阻塞的
g2=gevent.spawn(play,'rose')
g2.join()
g2.value #擷取傳回值,需要注意的是這個是一個屬性,需要在join後才能擷取到,不然就是None 此處列印 傳回值
import asyncio
async def func(): #協程方法
print("start")
await asyncio.sleep(1) #阻塞 需要一個關鍵字await告知是阻塞需要執行一個協程了,如果用await需要在函數之前加上async表示是一個協程函數
print("end")
return 123
async def func1():
print("start")
await asyncio.sleep(1)
print("end")
return 456
async def func2():
print("start")
await asyncio.sleep(1)
print("end")
return 789
#啟動一個任務
loop=asyncio.get_event_loop() #建立一個事件循環
loop.run_until_complete(func()) #把func任務丢到事件循環中去執行 執行一個協程函數 和join差不多意思
#啟動多個任務,并且沒有傳回值
loop=asyncio.get_event_loop()
wait_l=asyncio.wait([func(),func1(),func2()]) #執行多個協程函數
loop.run_until_complete(wait_l)
#啟動多個任務,并且有傳回值
loop=asyncio.get_event_loop()
f=loop.create_task(func())
f1=loop.create_task(func1())
f2=loop.create_task(func2())
task_l=[f,f1,f2] # 按照這個順序取傳回值
wait_l=asyncio.wait([f,f1,f2])
loop.run_until_complete(wait_l)
for i in task_l:
print(i.result()) #傳回值
#誰先回來先取誰的值
import asyncio
async def demo(i):
print("start")
await asyncio.sleep(10-i)
print("end")
return i,456
async def main():
task_l=[]
for i in range(10):
task = asyncio.ensure_future(demo(i))
task_l.append(task)
for ret in asyncio.as_completed(task_l):
res = await ret
print(res)
loop=asyncio.get_event_loop()
loop.run_until_complete(main())
"""
await 阻塞 協程函數這裡要切換出去,還能保證一會兒在切回來
await必須寫在async函數裡,async函數是協程函數
loop事件循環
所有的協程的執行,排程都離不開這個loop
"""
asyncio
a=0
def func():
global a
a+=1
import dis
dis.dis(func) #檢視func的cpu指令
dis
轉載于:https://www.cnblogs.com/bubu99/p/10171786.html