python使用協程與並發有什麼用?

青灯夜游
發布: 2018-09-21 15:19:55
原創
2579 人瀏覽過

本章為大家介紹python使用協程與並發的作用,讓大家了解使用協程的利弊,以及gevent並發框架的作用。有一定的參考價值,有需要的朋友可以參考一下,希望對你們有幫助。

協程

協程是一種使用者態的輕量級線程,又稱為微線程。

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

優點:

  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),
])
登入後複製

執行結果:

python使用協程與並發有什麼用?

  • #同步異步

  • 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()
    登入後複製
執行輸出:


python使用協程與並發有什麼用?

  • 以子類別的方法使用協程

可以子類化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操作完成,再在適當的時候切換回來繼續執行。

由於IO操作非常耗時,經常讓程式處於等待狀態,有了gevent為我們自動切換協程,就保證總有greenlet在運行,而不是等待IO。

由於切換是在IO操作時自動完成,所以gevent需要修改Python自帶的一些標準庫,這個過程在啟動時透過monkey patch完成

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/'),
])
登入後複製

執行輸出:


python使用協程與並發有什麼用?

可以看出3個網路操作是並發執行的,而且結束順序不同

以上是python使用協程與並發有什麼用?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板