搜尋
首頁後端開發Python教學詳解Python使用asyncio包處理併發的方法

詳解Python使用asyncio包處理併發的方法

Sep 11, 2017 am 10:59 AM
asynciopython處理

這篇文章主要為大家詳細介紹了Python使用asyncio包處理並發的相關資料,具有一定的參考價值,有興趣的小伙伴們可以參考一下

阻塞型I/ O和GIL

CPython 解釋器本身就不是線程安全的,因此有全域解釋器鎖定(GIL),一次只允許使用一個執行緒執行Python 字節碼。因此,一個 Python 進程通常不能同時使用多個 CPU 核心。

然而,標準函式庫中所有執行阻塞型 I/O 操作的函數,在等待作業系統回傳結果時都會釋放GIL。這意味著在 Python 語言這個層次上可以使用多線程,而 I/O 密集型 Python 程式能從中受益:當一個 Python 線程等待網路回應時,阻塞型 I/O 函數會釋放 GIL,再運行一個線程。

asyncio

這個套件使用事件循環驅動的協程實作並發。 asyncio 大量使用 yield from 表達式,因此與Python 舊版不相容。

asyncio 套件使用的「協程」是較嚴格的定義。適合asyncio API 的協程在定義體中必須使用 yield from,而不能使用 yield。此外,適合asyncio 的協程要由呼叫方驅動,並由呼叫方透過yield from 呼叫;

範例1


##

import threading
import asyncio

@asyncio.coroutine
def hello():
  print('Start Hello', threading.currentThread())
  yield from asyncio.sleep(5)
  print('End Hello', threading.currentThread())

@asyncio.coroutine
def world():
  print('Start World', threading.currentThread())
  yield from asyncio.sleep(3)
  print('End World', threading.currentThread())

# 获取EventLoop:
loop = asyncio.get_event_loop()
tasks = [hello(), world()]
# 执行coroutine
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

@asyncio.coroutine把生成器函數標記為協程型別。

asyncio.sleep(3) 建立一個3秒後完成的協程。
loop.run_until_complete(future),執行直到future完成;如果參數是 coroutine object,則需要使用 ensure_future()函式包裝。
loop.close() 關閉事件循環

範例2


#

import asyncio

@asyncio.coroutine
def worker(text):
  """
  协程运行的函数
  :param text:
  :return:
  """
  i = 0
  while True:
    print(text, i)

    try:
      yield from asyncio.sleep(.1)
    except asyncio.CancelledError:
      break

    i += 1


@asyncio.coroutine
def client(text, io_used):
  worker_fu = asyncio.ensure_future(worker(text))

  # 假装等待I/O一段时间
  yield from asyncio.sleep(io_used)

  # 结束运行协程
  worker_fu.cancel()
  return 'done'


loop = asyncio.get_event_loop()
tasks = [client('xiaozhe', 3), client('zzzz', 5)]
result = loop.run_until_complete(asyncio.wait(tasks))
loop.close()
print('Answer:', result)

解釋:


1. asyncio.ensure_future(coro_or_future, *, loop=None):計畫安排一個coroutine object的執行,回傳一個asyncio.Task object。

2. worker_fu.cancel(): 取消一個協程的執行,拋出CancelledError例外。
3. asyncio.wait():協程的參數是一個由期物或協程構成的可迭代物件; wait 會分別把各個協程包裝進一個 Task 物件。

asyncio.Task 物件與threading.Thread物件的比較

asyncio.Task 物件差不多與 threading.Thread 物件等效。

Task 物件用於驅動協程, Thread 物件用於呼叫可呼叫的物件。
Task 物件不是由自己動手實例化,而是透過把協程傳給 asyncio.ensure_future(…) 函數或loop.create_task(…) 方法來取得。
取得的 Task 物件已經排定了運行時間;Thread 實例則必須呼叫 start 方法,明確告知讓它運行。
如果想要終止任務,可以使用 Task.cancel() 實例方法,在協程內部拋出CancelledError 例外。

執行緒與協程的安全性比較

如果使用執行緒做過重要的編程,因為排程器任何時候都能中斷執行緒。必須記住保留鎖,去保護程式中的重要部分,防止多步驟操作在執行的過程中中斷,防止資料處於無效狀態。

協程預設會做好全方位保護,以防止中斷。我們必須顯式產出才能讓程式的餘下部分運作。對協程來說,無須保留鎖,在多個執行緒之間同步操作,協程本身就會同步,因為在任意時刻只有一個協程運行。想交出控制權時,可以使用 yield 或 yield from 把控制權交還調度程式。這就是能夠安全地取消協程的原因:按照定義,協程只能在暫停的 yield處取消,因此可以處理 CancelledError 異常,執行清理操作。

Future(期物)

通常自己不應該建立期物,而只能由並發框架(concurrent.futures 或 asyncio)實例化。原因很簡單:期物表示終將發生的事情,而決定某件事會發生的唯一方式是執行的時間已經排定。

asyncio.Future

在asyncio 套件中, BaseEventLoop.create_task(…) 方法接收一個協程,排定它的運行時間,然後傳回一個asyncio. Task 實例-也是asyncio.Future 類別的實例,因為Task 是Future 的子類,用來包裝協程。

asyncio.ensure_future(coro_or_future, *, loop=None)

這個函數統一了協程和期物:第一個參數可以是二者中的任何一個。如果是 Future 或 Task 對象,那就原封不動地回傳。如果是協程,那麼 async 函數會呼叫loop.create_task(…) 方法建立 Task 物件。 loop= 關鍵字參數是可選的,用於傳入事件循環;如果沒有傳入,那麼 async 函數會透過呼叫 asyncio.get_event_loop() 函數來取得循環物件。

BaseEventLoop.create_task(coro)

這個方法排定協程的執行時間,傳回一個 asyncio.Task 物件。

asyncio 套件中有多個函式會自動把參數指定的協程包裝在 asyncio.Task 物件中,例如 BaseEventLoop.run_until_complete(…) 方法。

asyncio.as_completed#

为了集成进度条,我们可以使用的是 as_completed 生成器函数;幸好, asyncio 包提供了这个生成器函数的相应版本。

使用asyncio和aiohttp包

从 Python 3.4 起, asyncio 包只直接支持 TCP 和 UDP。如果想使用 HTTP 或其他协议,那么要借助第三方包 aiohttp 。


cc_list = ['China', 'USA']

@asyncio.coroutine
def get_flag(cc):
  url = '{}/{cc}/{cc}.gif'.format(BASE_URL, cc=cc.lower())
  resp = yield from aiohttp.request('GET', url)
  image = yield from resp.read()
  return image

@asyncio.coroutine
def download_one(name): 
  image = yield from get_flag(name) 
  save_flag(image, name.lower() + '.gif')
  return name

loop = asyncio.get_event_loop() 
wait_coro = asyncio.wait([download_one(cc) for cc in sorted(cc_list)]) 
res, _ = loop.run_until_complete(wait_coro) 
loop.close()

使用 asyncio 包时,我们编写的异步代码中包含由 asyncio 本身驱动的协程(即委派生成器),而生成器最终把职责委托给 asyncio 包或第三方库(如aiohttp)中的协程。这种处理方式相当于架起了管道,让 asyncio 事件循环(通过我们编写的协程)驱动执行低层异步 I/O 操作的库函数。

避免阻塞型调用

有两种方法能避免阻塞型调用中止整个应用程序的进程:
1. 在单独的线程中运行各个阻塞型操作
2. 把每个阻塞型操作转换成非阻塞的异步调用使用

多个线程是可以的,但是各个操作系统线程(Python 使用的是这种线程)消耗的内存达兆字节(具体的量取决于操作系统种类)。如果要处理几千个连接,而每个连接都使用一个线程的话,我们负担不起。

把生成器当作协程使用是异步编程的另一种方式。对事件循环来说,调用回调与在暂停的协程上调用 .send() 方法效果差不多。各个暂停的协程是要消耗内存,但是比线程消耗的内存数量级小。

上面的脚本为什么会很快

在上面的脚本中,调用 loop.run_until_complete 方法时,事件循环驱动各个download_one 协程,运行到第一个 yield from 表达式处时,那个表达式驱动各个get_flag 协程,然后在get_flag协程里面运行到第一个 yield from 表达式处时,调用 aiohttp.request(…)函数。这些调用都不会阻塞,因此在零点几秒内所有请求全部开始。

asyncio 的基础设施获得第一个响应后,事件循环把响应发给等待结果的 get_flag 协程。得到响应后, get_flag 向前执行到下一个 yield from 表达式处,调用resp.read() 方法,然后把控制权还给主循环。其他响应会陆续返回。所有 get_ flag 协程都获得结果后,委派生成器 download_one 恢复,保存图像文件。

async和await

为了简化并更好地标识异步IO,从Python 3.5开始引入了新的语法async和await,可以让coroutine的代码更简洁易读。

async和await是针对coroutine的新语法,要使用新的语法,只需要做两步简单的替换。
1. 把@asyncio.coroutine替换为async
2. 把yield from替换为await

例如:


@asyncio.coroutine
def hello():
  print("Hello world!")
  r = yield from asyncio.sleep(1)
  print("Hello again!")

等同于


async def hello():
  print("Hello world!")
  r = await asyncio.sleep(1)
  print("Hello again!")

网站请求实例


import asyncio
import aiohttp

urls = [
  'http://www.163.com/',
  'http://www.sina.com.cn/',
  'https://www.hupu.com/',
  'http://www.php.cn/'
]


async def get_url_data(u):
  """
  读取url的数据
  :param u:
  :return:
  """
  print('running ', u)
  async with aiohttp.ClientSession() as session:
    async with session.get(u) as resp:
      print(u, resp.status, type(resp.text()))
      # print(await resp.text())

  return resp.headers


async def request_url(u):
  """
  主调度函数
  :param u:
  :return:
  """
  res = await get_url_data(u)
  return res


loop = asyncio.get_event_loop()
task_lists = asyncio.wait([request_url(u) for u in urls])
all_res, _ = loop.run_until_complete(task_lists)
loop.close()

print(all_res)

以上是詳解Python使用asyncio包處理併發的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Python與C:學習曲線和易用性Python與C:學習曲線和易用性Apr 19, 2025 am 12:20 AM

Python更易學且易用,C 則更強大但複雜。 1.Python語法簡潔,適合初學者,動態類型和自動內存管理使其易用,但可能導致運行時錯誤。 2.C 提供低級控制和高級特性,適合高性能應用,但學習門檻高,需手動管理內存和類型安全。

Python vs. C:內存管理和控制Python vs. C:內存管理和控制Apr 19, 2025 am 12:17 AM

Python和C 在内存管理和控制方面的差异显著。1.Python使用自动内存管理,基于引用计数和垃圾回收,简化了程序员的工作。2.C 则要求手动管理内存,提供更多控制权但增加了复杂性和出错风险。选择哪种语言应基于项目需求和团队技术栈。

科學計算的Python:詳細的外觀科學計算的Python:詳細的外觀Apr 19, 2025 am 12:15 AM

Python在科學計算中的應用包括數據分析、機器學習、數值模擬和可視化。 1.Numpy提供高效的多維數組和數學函數。 2.SciPy擴展Numpy功能,提供優化和線性代數工具。 3.Pandas用於數據處理和分析。 4.Matplotlib用於生成各種圖表和可視化結果。

Python和C:找到合適的工具Python和C:找到合適的工具Apr 19, 2025 am 12:04 AM

選擇Python還是C 取決於項目需求:1)Python適合快速開發、數據科學和腳本編寫,因其簡潔語法和豐富庫;2)C 適用於需要高性能和底層控制的場景,如係統編程和遊戲開發,因其編譯型和手動內存管理。

數據科學和機器學習的Python數據科學和機器學習的PythonApr 19, 2025 am 12:02 AM

Python在數據科學和機器學習中的應用廣泛,主要依賴於其簡潔性和強大的庫生態系統。 1)Pandas用於數據處理和分析,2)Numpy提供高效的數值計算,3)Scikit-learn用於機器學習模型構建和優化,這些庫讓Python成為數據科學和機器學習的理想工具。

學習Python:2小時的每日學習是否足夠?學習Python:2小時的每日學習是否足夠?Apr 18, 2025 am 12:22 AM

每天學習Python兩個小時是否足夠?這取決於你的目標和學習方法。 1)制定清晰的學習計劃,2)選擇合適的學習資源和方法,3)動手實踐和復習鞏固,可以在這段時間內逐步掌握Python的基本知識和高級功能。

Web開發的Python:關鍵應用程序Web開發的Python:關鍵應用程序Apr 18, 2025 am 12:20 AM

Python在Web開發中的關鍵應用包括使用Django和Flask框架、API開發、數據分析與可視化、機器學習與AI、以及性能優化。 1.Django和Flask框架:Django適合快速開發複雜應用,Flask適用於小型或高度自定義項目。 2.API開發:使用Flask或DjangoRESTFramework構建RESTfulAPI。 3.數據分析與可視化:利用Python處理數據並通過Web界面展示。 4.機器學習與AI:Python用於構建智能Web應用。 5.性能優化:通過異步編程、緩存和代碼優

Python vs.C:探索性能和效率Python vs.C:探索性能和效率Apr 18, 2025 am 12:20 AM

Python在開發效率上優於C ,但C 在執行性能上更高。 1.Python的簡潔語法和豐富庫提高開發效率。 2.C 的編譯型特性和硬件控制提升執行性能。選擇時需根據項目需求權衡開發速度與執行效率。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境