Python 攜程


一、協程

  1、又稱微線程,纖程。英文名Coroutine.一句話說明什么是協程:協程是一種用戶態的輕量級線程(相當於操作系統不知道它的存在,是用戶控制的)。

  2、協程擁有自己的寄存器上下文和棧(代碼的必要的代碼段和)。協程調度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧,因此:協程能保留上一次調用時的狀態(即所有局部狀態的一個特定組合),每次過程重入時,就相當於進入上一次調用的狀態,換種說法:進入上一次離開時所處邏輯流的位置。

  3、協程一定是在單線程中運行的。

二、協程的優點與缺點

  優點:

  1、無需線程上下文切換的開銷。

  2、無需原子操作(最小級別的操作)鎖定及同步的開銷。

  3、方便切換控制流,簡化編程模型。

  4、高並發+高擴展性+低成本:一個CPU支持上萬的協程都不是問題,所以很適合用於高並發處理。

  缺點:

  1、無法利用多核資源:協程的本質是個單線程,它不能同時將單個CPU的多個核用上,協程需要和進程配合才能運行在多CPU上,當然我們日常所編寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用。

  2、進行阻塞(Blocking)操作(如IO時)會阻塞掉整個程序。

三、使用yield實現協程操作例子

  1、使用yield實現的一個最簡單的協程的效果

 1 #!/usr/bin/python
 2 # -*- coding : utf-8 -*-
 3 # 作者: Presley
 4 # 時間: 2018-12-4
 5 # 郵箱:1209989516@qq.com
 6 # 這是我用來練習python 協程的測試腳本
 7 
 8 import time
 9 import queue
10 
11 def consumer(name):
12     print("starting eating baozi...")
13     while True:
14         new_baozi = yield
15         print("[%s] is eating baozi %s" %(name,new_baozi))
16 
17 def producer():
18     r = con.__next__()
19     r = con2.__next__()
20     n = 0
21     while n < 5:
22         n += 1
23         con.send(n)
24         con2.send(n)
25         print("\033[32;1m[producer]\033[0m is making")
26 
27 if __name__ == "__main__":
28     con = consumer("c1")
29     con2 = consumer("c2")
30 
31 
32     p = producer()

  執行結果:

 1 C:\Users\wohaoshuai\AppData\Local\Programs\Python\Python36\python.exe E:/PythonLearn/day16/pro_consume.py
 2 starting eating baozi...
 3 starting eating baozi...
 4 [c1] is eating baozi 1
 5 [c2] is eating baozi 1
 6 [producer] is making
 7 [c1] is eating baozi 2
 8 [c2] is eating baozi 2
 9 [producer] is making
10 [c1] is eating baozi 3
11 [c2] is eating baozi 3
12 [producer] is making
13 [c1] is eating baozi 4
14 [c2] is eating baozi 4
15 [producer] is making
16 [c1] is eating baozi 5
17 [c2] is eating baozi 5
18 [producer] is making
19 
20 Process finished with exit code 0

   2、greenlet

 1 #!/usr/bin/python
 2 # -*- coding : utf-8 -*-
 3 # 作者: Presley
 4 # 時間: 2018-12-4
 5 # 郵箱:1209989516@qq.com
 6 # 這是我用來練習python 協程的測試腳本
 7 
 8 from greenlet import greenlet
 9 
10 def test1():
11     print(12)
12     gr2.switch() 
13     print(34)
14     gr2.switch()
15 
16 def test2():
17     print(56)
18     gr1.switch()
19     print(78)
20 
21 gr1 = greenlet(test1)
22 gr2 = greenlet(test2)
23 gr1.switch()

執行結果:

1 C:\Users\wohaoshuai\AppData\Local\Programs\Python\Python36\python.exe E:/PythonLearn/day16/pro_consume.py
2 12
3 56
4 34
5 78
6 
7 Process finished with exit code 0

  3、gevent

    a、gevent是一個第三方庫,可以輕松通過gevent實現並發同步或異步編程,在gevent中用到的主要模式是greenlet,它是以C擴展模塊形式接入Python的輕量級協程。greenlet全部運行在主程序操作系統進程的內部,但它們被協作式地調度。

 1 #!/usr/bin/python
 2 # -*- coding : utf-8 -*-
 3 # 作者: Presley
 4 # 時間: 2018-12-1
 5 # 郵箱:1209989516@qq.com
 6 # 這是我用來練習python 協程的測試腳本
 7 
 8 import gevent
 9 
10 def foo():
11     print("Running in foo")
12     gevent.sleep(1)
13     print("Explicit context switch to foo again")
14 
15 def bar():
16     print("Explicit context to bar")
17     gevent.sleep(1)
18     print("Implicit context switch back to bar")
19 
20 def ex():
21     print("Explicit context to ex")
22     gevent.sleep(1)
23     print("Implicit context switch back to ex")
24 
25 gevent.joinall([
26     gevent.spawn(foo),  #類似產生一個協程的foo
27     gevent.spawn(bar),  #產生一個協程的bar
28     gevent.spawn(ex)
29 ])
30 
31 #代碼的效果為:第一個協程切換到第二個,第二個切換到第三個,然后又遇到sleep(模擬io)又切換到下一個,然后實現並發的協程的效果

    執行結果

1 C:\Users\wohaoshuai\AppData\Local\Programs\Python\Python36\python.exe E:/PythonLearn/day16/pro_consume.py
2 Running in foo
3 Explicit context to bar
4 Explicit context to ex
5 Explicit context switch to foo again
6 Implicit context switch back to bar
7 Implicit context switch back to ex
8 
9 Process finished with exit code 0

   b、通過協程爬取網頁實例

 1 #!/usr/bin/python
 2 # -*- coding : utf-8 -*-
 3 # 作者: Presley
 4 # 時間: 2018-12-5
 5 # 郵箱:1209989516@qq.com
 6 # 這是我用來練習python 協程的測試腳本
 7 
 8 from gevent import monkey;monkey.patch_all()
 9 import gevent
10 
11 from urllib.request import urlopen
12 
13 def f(url):
14     print("GET: %s"  %url)
15     resp = urlopen(url)
16     data = resp.read()
17     print("%d bytes received from %s." %(len(data),url))
18 
19 gevent.joinall([
20     gevent.spawn(f,"https://www.python.org/"),
21     gevent.spawn(f,"https://www.yahoo.com/"),
22     gevent.spawn(f,"https://github.com"),
23 ])

執行結果:

1 C:\Users\wohaoshuai\AppData\Local\Programs\Python\Python36\python.exe E:/PythonLearn/day16/pro_consume.py
2 GET: https://www.python.org/
3 GET: https://www.yahoo.com/
4 GET: https://github.com
5 80704 bytes received from https://github.com.
6 50008 bytes received from https://www.python.org/.
7 528149 bytes received from https://www.yahoo.com/.
8 
9 Process finished with exit code 0

  c、通過gevent實現單線程下的多socket並發

    server端

 1 #!/usr/bin/python
 2 # -*- coding : utf-8 -*-
 3 # 作者: Presley
 4 # 時間: 2018-12-5
 5 # 郵箱:1209989516@qq.com
 6 # 這是我用來練習python 協程的測試腳本
 7 
 8 
 9 import gevent
10 from gevent import socket,monkey
11 monkey.patch_all() #python中的一種黑魔法,只要寫入一句話就自動的把python中的許多標准庫變為非阻塞的模式
12 
13 def server(port):
14     s = socket.socket()
15     s.bind(("0.0.0.0",port))
16     s.listen(5000)
17     while True:
18         cli,addr = s.accept()
19         gevent.spawn(handle_request,cli) #執行handle_request函數,參數是cli,即客戶端實例
20 def handle_request(s):
21     try:
22         while True:
23             data = s.recv(1024) #接收數據,這里設置成不阻塞
24             print("recv:",data)
25             s.send(data)
26             if not data:
27                 s.shutdown(socket.SHUT_RD) #如果接收為空值,結束
28     except Exception as ex:
29         print(ex)
30     finally:
31         s.close()
32 
33 if __name__ == "__main__":
34     server(8001)

    client端

 1 #!/usr/bin/python
 2 # -*- coding : utf-8 -*-
 3 # 作者: Presley
 4 # 時間: 2018-12-5
 5 # 郵箱:1209989516@qq.com
 6 # 這是我用來練習python 協程的測試腳本
 7 
 8 import socket
 9 
10 HOST = "localhost"
11 PORT = 8001
12 s = socket.socket()
13 s.connect((HOST,PORT))
14 
15 while True:
16     msg = input(">>:")
17     if not msg:continue
18     msg = msg.encode("utf-8")
19     s.sendall(msg)
20     data = s.recv(1024)
21     print("Received",data.decode("utf-8"))
22 s.close()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM