python編程使用協程併發的優缺點

python編程使用協程併發的優缺點

協程

協程是一種用戶態的輕量級線程,又稱微線程。python

協程擁有本身的寄存器上下文和棧,調度切換時,將寄存器上下文和棧保存到其餘地方,>在切回來的時候,恢復先前保存的寄存器上下文和棧。所以:協程能保留上一次調用時的>狀態(即全部局部狀態的一個特定組合),每次過程重入時,就至關於進入上一次調用的>狀態,換種說法:進入上一次離開時所處邏輯流的位置.shell

優勢:

  • 1.無需線程上下文切換的開銷
  • 2.無需原子操做鎖定及同步的開銷
  • 3.方便切換控制流,簡化編程模型
  • 4.高併發+高擴展性+低成本:一個CPU支持上萬的協程都不是問題。因此很適合用於高併發處理。

所謂原子操做是指不會被線程調度機制打斷的操做;這種操做一旦開始,就一直運行到結束,中間不會有任何 context switch (切換到另外一個線程)。原子操做能夠是一個步驟,也能夠是多個操做步驟,可是其順序是不能夠被打亂,或者切割掉只執行部分。視做總體是原子性的核心編程

缺點:

  • 1.沒法利用多核資源:協程的本質是個單線程,它不能同時將 單個CPU 的多個核用上,協程須要和進程配合才能運行在多CPU上.固然咱們平常所編寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用。
  • 2.進行阻塞(Blocking)操做(如IO時)會阻塞掉整個程序

使用Gevent

gevent是python的一個併發框架,以微線程greenlet爲核心,使用了epoll事件監聽機制以及諸多其餘優化而變得高效.網絡

簡單示例

gevent的sleep能夠交出控制權,當咱們在受限於網絡或IO的函數中使用gevent,這些函數會被協做式的調度, gevent的真正能力會獲得發揮。Gevent處理了全部的細節, 來保證你的網絡庫會在可能的時候,隱式交出greenlet上下文的執行權併發

import gevent
def foo():
  print('running in foo')
  gevent.sleep(0)
  print('com back from bar in to foo')
def bar():
  print('running in bar')
  gevent.sleep(0)
  print('com back from foo in to bar')
# 建立線程並行執行程序
gevent.joinall([
  gevent.spawn(foo),
  gevent.spawn(bar),
])

# 執行結果:
#running in foo
#running in bar
#com back from bar in to foo
#com back from foo in to bar

同步異步

import random
import gevent
def task(pid):
  gevent.sleep(random.randint(0, 2) * 0.001)
  print('Task %s done' % pid)
def synchronous():
  for i in range(1, 10):
    task(i)
def asynchronous():
  threads = [gevent.spawn(task, i) for i in range(10)]
  gevent.joinall(threads)
print('Synchronous:')
synchronous()
print('Asynchronous:')
asynchronous()

以子類的方法使用協程

能夠子類化Greenlet類,重載它的_run方法,相似多線線程和多進程模塊框架

import gevent
from gevent import Greenlet
class Test(Greenlet):
  def __init__(self, message, n):
    Greenlet.__init__(self)
    self.message = message
    self.n = n
  def _run(self):
    print(self.message, 'start')
    gevent.sleep(self.n)
    print(self.message, 'end')
tests = [
  Test("hello", 3),
  Test("world", 2),
]
for test in tests:
  test.start() # 啓動
for test in tests:
  test.join() # 等待執行結束

使用monkey patch修改系統標準庫(自動切換協程)

當一個greenlet遇到IO操做時,好比訪問網絡,就自動切換到其餘的greenlet,等到IO操做完成,再在適當的時候切換回來繼續執行。dom

因爲IO操做很是耗時,常常使程序處於等待狀態,有了gevent爲咱們自動切換協程,就保證總有greenlet在運行,而不是等待IO。異步

因爲切換是在IO操做時自動完成,因此gevent須要修改Python自帶的一些標準庫,這一過程在啓動時經過monkey patch完成socket

import gevent
import requests
from gevent import monkey
monkey.patch_socket()
def task(url):
  r = requests.get(url)
  print('%s bytes received from %s' % (len(r.text), url))
gevent.joinall([
  gevent.spawn(task, 'https://www.baidu.com/'),
  gevent.spawn(task, 'https://www.qq.com/'),
  gevent.spawn(task, 'https://www.jd.com/'),
])

執行輸出async

2443 bytes received from https://www.baidu.com/

108315 bytes received from https://www.jd.com/

231873 bytes received from https://www.qq.com/

能夠看出3個網絡操做是併發執行的,並且結束順序不一樣
相關文章
相關標籤/搜索