1. 程式人生 > 實用技巧 >13.併發程式設計之協程

13.併發程式設計之協程

目錄

一、協程基礎

cpython下多個執行緒不能利用多核:規避了所有的io操作的單執行緒。

協程

  • 作業系統不可見
  • 協程本質就是一條執行緒,多個任務在一條執行緒上來回切換,來規避io操作,降低了執行緒上的io操作降到最低。

4 cpu:可以接受5個程序,20個執行緒,協程500

切換並規避IO的模組

  • gevent 利用 greenlet 底層模組完成的切換 + 自動規避IO的功能。
  • asyncio 利用 yield 底層語法完成的切換 + 自動規避IO的功能。
    • tornado 非同步的web框架
    • yield from 更好的實現協程
    • send 更好的實現協程
    • asyncio模組,基於python原生的協程的概念正式被成立
    • 特殊的在python中提供協程的功能的關鍵字,aysnc await

程序、執行緒、協程之間區別:

程序 資料隔離 資料不安全 作業系統級別 開銷最大 能利用多核
執行緒 資料共享 資料不安全 作業系統級別 開銷較小 不能利用多核 一些和檔案操作相關的io只有作業系統能感知到
協程 資料共享 資料安全 使用者級別 開銷非常小 不能利用多核 協程的所有切換都基於使用者只有在使用者級別能感知到的操作才會用協程模組做切換

使用者級別的協程有什麼好處?減輕作業系統的負擔,一條執行緒開了多個協程,那麼給作業系統的印象是執行緒很忙,這樣能多爭取一些時間來被cpu執行,程式的效率提高了。

二、gevent模組

協程之間是併發的,使用time.sleep()就不是併發的了。

import gevent

def func():     # 帶有io操作的內容解除安裝函式中,然後提交func給gevent
    print('start func')
    gevent.sleep(1)
    print('end func')

g1 = gevent.spawn(func)
g2 = gevent.spawn(func)
g3 = gevent.spawn(func)
gevent.joinall([g1, g2, g3])

# g1.join()       # 阻塞,知道協程g1任務執行結束
# g2.join() 
# g3.join() 

from gevnet import monkey必須寫在前面,不然time.sleep()就是同步的了。

from gevent import monkey
monkey.patch_all()
import gevent
import time

def func():     # 帶有io操作的內容解除安裝函式中,然後提交func給gevent
    print('start func')
    time.sleep(1)
    print('end func')

g1 = gevent.spawn(func)
g2 = gevent.spawn(func)
g3 = gevent.spawn(func)
gevent.joinall([g1, g2, g3])

檢測一個模組在gevent處能不能規避io?

import socket
print(socket.socket)			  # 在patch_all 之前列印一次
from gevent import monkey		# gevent 如何檢測是否能規避某個模組的io操作?
monkey.patch_all()
import socket
import gevent
print(socket.socket)				# 在path_all 之後列印一次,如果兩次不一樣,那麼就說明能規避io操作

三、asyncio模組

import asyncio
async def func(name):
    print('start', name)
    # await 可能會發生阻塞的方法
    # await 關鍵字必須解除安裝一個asyncio函式中
    await asyncio.sleep(1)
    print('end')
loop = asyncio.get_event_loop()
# 同步
# loop.run_until_complete(func('an'))
# 非同步
loop.run_until_complete(asyncio.wait([func('an'), func('bn')]))