1. 程式人生 > >python-day37--協程

python-day37--協程

view reading 不能 應用程序級別 upper switch receive cer 控制

一、 協程介紹

單線程下實現並發,提升運行效率,

  1.自己控制切換,保存狀態

  2.遇到I/O切 (單純的CPU切沒意義,只有在遇到I/O的時候切才有效率)

一句話說明什麽是線程:協程是一種用戶態的輕量級線程,即協程是由用戶程序自己控制調度的。、

需要強調的是:

#1. python的線程屬於內核級別的,即由操作系統控制調度(如單線程遇到io或執行時間過長就會被迫交出cpu執行權限,切換其他線程運行)
#2. 單線程內開啟協程,一旦遇到io,就會從應用程序級別(而非操作系統)控制切換,以此來提升效率(!!!非io操作的切換與效率無關)

對比操作系統控制線程的切換,用戶在單線程內控制協程的切換

優點如下:

#1. 協程的切換開銷更小,屬於程序級別的切換,操作系統完全感知不到,因而更加輕量級
#2. 單線程內就可以實現並發的效果,最大限度地利用cpu

缺點如下:

#1. 協程的本質是單線程下,無法利用多核,可以是一個程序開啟多個進程,每個進程內開啟多個線程,每個線程內開啟協程
#2. 協程指的是單個線程,因而一旦協程出現阻塞,將會阻塞整個線程

總結協程特點:

  1. 必須在只有一個單線程裏實現並發
  2. 修改共享數據不需加鎖
  3. 用戶程序裏自己保存多個控制流的上下文棧
  4. 附加:一個協程遇到IO操作自動切換到其它協程(如何實現檢測IO,yield、greenlet都無法實現,就用到了gevent模塊(select機制))

二、用yeild實現協程

技術分享
 1 import time
 2 def init(func):
 3     def wrapper(*args,**kwargs):
 4         g=func(*args,**kwargs)
 5         next(g)
 6         return g
 7     return wrapper
 8 @init
 9 def consumer():
10     while True:
11         x=yield
12         print(x)
13 
14 def producer(target):
15     for
i in range(10): 16 # time.sleep(1) 17 target.send(i) 18 19 producer(consumer())
View Code

三、greenlet模塊(了解就行,遇到I/O不能切)

技術分享
 1 from greenlet import greenlet
 2 import time
 3 def eat(name):
 4     print(%s eat 1 %name)
 5     time.sleep(10)      #在io阻塞的時候 不能來回切,會一直在這睡著
 6     g2.switch(egon)
 7     print(%s eat 2 %name)
 8     g2.switch()
 9 def play(name):
10     print(%s play 1 %name)
11     g1.switch()
12     print(%s play 2 %name)
13 
14 g1=greenlet(eat)
15 g2=greenlet(play)
16 
17 #g1.switch(‘egon‘)#可以在第一次switch時傳入參數,以後都不需要
View Code

四、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的返回值
 1 from gevent import monkey;monkey.patch_all()    #monkey;monkey.patch_all()  可以識別其他的I/O操作
 2 import gevent
 3 import time,threading
 4 def eat(name):
 5     print(%s eat 1 %name)
 6     time.sleep(2)
 7     print(%s eat 2 %name)
 8     return eat
 9 
10 def play(name):
11     print(%s play 1 %name)
12     time.sleep(3)
13     print(%s play 2 %name)
14     return play
15 
16 start=time.time()
17 g1=gevent.spawn(eat,egon)
18 g2=gevent.spawn(play,egon)
19 # g1.join()
20 # g2.join()
21 gevent.joinall([g1,g2])
22 print(,(time.time()-start))
23 print(g1.value)
24 print(g2.value)
25 
26 # 結果:
27 egon eat 1
28 egon play 1
29 egon eat 2
30 egon play 2
31 主 3.0018091201782227
32 eat
33 play

五、協程的應用

練習題1

技術分享
 1 from gevent import monkey;monkey.patch_all()
 2 import gevent
 3 import requests
 4 import time
 5 
 6 def get_page(url):
 7     print(GET: %s %url)
 8     response=requests.get(url)
 9     if response.status_code == 200:
10         print(%d bytes received from %s %(len(response.text),url))
11 
12 start_time=time.time()
13 
14 # get_page(‘https://www.python.org/‘)   #串行
15 # get_page(‘https://www.yahoo.com/‘)    #串行
16 # get_page(‘https://github.com/‘)   #串行
17     
18 g1=gevent.spawn(get_page, https://www.python.org/)
19 g2=gevent.spawn(get_page, https://www.yahoo.com/)
20 g3=gevent.spawn(get_page, https://github.com/)
21 
22 gevent.joinall([g1,g2,g3])
23 stop_time=time.time()
24 print(run time is %s %(stop_time-start_time))
爬蟲練習

練習題2

技術分享
 1 from gevent import monkey;monkey.patch_all()
 2 import gevent
 3 from socket import *
 4 def talk(conn,addr):
 5     while True:
 6         data=conn.recv(1024)
 7         print(%s:%s %s %(addr[0],addr[1],data))
 8         conn.send(data.upper())
 9     conn.close()
10 
11 def server(ip,port):
12     s = socket(AF_INET, SOCK_STREAM)
13     s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
14     s.bind((ip,port))
15     s.listen(5)
16     while True:
17         conn,addr=s.accept()
18         gevent.spawn(talk,conn,addr)
19     s.close()
20 
21 if __name__ == __main__:
22     server(127.0.0.1, 8088)
並發通信 服務端 技術分享
 1 from multiprocessing import Process
 2 from socket import *
 3 def client(server_ip,server_port):
 4     client=socket(AF_INET,SOCK_STREAM)
 5     client.connect((server_ip,server_port))
 6     while True:
 7         cmd=input(‘‘)
 8         client.send(cmd.encode(utf-8))
 9         msg=client.recv(1024)
10         print(msg.decode(utf-8))
11 
12 if __name__ == __main__:
13     client(127.0.0.1, 8088)
並發通信 客戶端

python-day37--協程