1. 程式人生 > >python-day35(協程,IO多路複用)

python-day35(協程,IO多路複用)

一. 協程

  協程: 是單執行緒下的併發,又稱微執行緒,纖程,英文名Coroutine. 

  併發: 切換+儲存狀態

    協程是一種使用者態的輕量級執行緒,即協程是由使用者程式自己控制排程的

  協程特點:

    1. 必須在只有一個單執行緒裡實現併發

    2. 修改共享資料不需要加鎖

    3. 使用者程式自己儲存多個控制流的上線文棧

    4. 一個協程遇到IO操作自動切換到其他協程(如何實現檢測IO,yield,greenlet都無法實現,

      就用到了gevent模組(select機制))

  Greenlet模組

    單純的切換(在沒有IO的情況下或者沒有重複開闢記憶體空間的操作),反而會降低程式的執行速度

  #真正的協程模組就是使用greenlet完成的切換
from greenlet import greenlet

def eat(name):
    print('%s eat 1' %name)  #2
    g2.switch('taibai')   #3
    print('%s eat 2' %name) #6
    g2.switch() #7
def play(name):
    print('%s play 1' %name) #4
    g1.switch()      #5
    print('%s play 2' %name) #8

g1=greenlet(eat)
g2
=greenlet(play) g1.switch('taibai')#可以在第一次switch時傳入引數,以後都不需要 1
#安裝
pip install greenlet
 1 # import time
 2 #
 3 # import greenlet
 4 # def fun1():
 5 #     time.sleep(2)
 6 #     print('約嗎?')
 7 #     g2.switch()
 8 #     time.sleep(2)
 9 #     print('不約')
10 #     g2.switch()
11
# def fun2(): 12 # time.sleep(2) 13 # print('你好') 14 # g1.switch() 15 # time.sleep(2) 16 # print('你不好') 17 # 18 # g1 = greenlet.greenlet(fun1) 19 # g2 = greenlet.greenlet(fun2) 20 # g1.switch() 21 # ---------------------------------------------------- 22 # import time 23 # import greenlet 24 # def f1(name): 25 # print(name) 26 # time.sleep(2) 27 # a = g2.switch(name+'1') 28 # print(a) 29 # 30 # def f2(name): 31 # print(name) 32 # time.sleep(2) 33 # g1.switch(name+'1') 34 # 35 # a = time.time() 36 # g1 = greenlet.greenlet(f1) 37 # g2 = greenlet.greenlet(f2) 38 # g1.switch('hui') 39 # b = time.time() 40 # print(b-a)
greenlet練習

 

  Gevent模組

#安裝
pip install gevent
#用法
g1 = gevent.spawn(func,多個引數) 建立一個協程物件g1, spawn(函式名,多個引數)
spawn是非同步提交任務

g2 = gevent.spawn(func2)

g1.join() #等待g1結束,上面只是建立協程物件,這個join才是去執行
g2.join() #等待g2結束,

gevent.joinall([g1,g2]) #等待列表中的協程都結束
g1.value #拿到func1的返回值
gevent.sleep(2)# 睡2秒 遇到IO切換
# 不能識別time.sleep 解決辦法 from gevent import monkey;monkey.patch_all()

  遇到IO阻塞是自動切換任務

import gevent
def eat(name):
    print('%s eat 1' %name)
    gevent.sleep(2)
    print('%s eat 2' %name)

def play(name):
    print('%s play 1' %name)
    gevent.sleep(1)
    print('%s play 2' %name)


g1=gevent.spawn(eat,'egon')
g2=gevent.spawn(play,name='egon')
g1.join()
g2.join()
#或者gevent.joinall([g1,g2])
print('')

  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 # import gevent
 2 # import time
 3 # #gevent 不能識別 time.sleep
 4 # # 解決辦法 from gevent import monkey;monkey.patch_all() 識別所有的IO 自動切換
 5 #
 6 # def func(n,):
 7 #     print('xxxx',n)
 8 #     gevent.sleep(2)
 9 #     # time.sleep(2) #不能識別
10 #     print('ccccc')
11 #
12 # def func2(m):
13 #     print('11111111',m)
14 #     gevent.sleep(2)
15 #     print('22222222')
16 # g1 = gevent.spawn(func,'alex')  #  非同步提交任務
17 # g2 = gevent.spawn(func2,'馬來西亞')
18 #
19 # # gevent.joinall([g1,g2]) #等多所有
20 # g1.join() #執行並等待非同步提交的任務完成
21 # g2.join()#執行並等待非同步提交的任務完成
22 # print('程式碼結束')
from gevent import monkey;monkey.patch_all()

 

二. IO多路複用

  IO模型

#1)  等待資料準備(Waiting for the data to be ready)
#2)  將資料從核心拷貝到程序中(Copying the data from the kernel to the process)

  1. 阻塞IO

  2. 非阻塞IO模型

  3. IO多路複用

  4. 非同步IO