1. 程式人生 > 程式設計 >python 單執行緒和非同步協程工作方式解析

python 單執行緒和非同步協程工作方式解析

在python3.4之後新增了asyncio模組,可以幫我們檢測IO(只能是網路IO【HTTP連線就是網路IO操作】),實現應用程式級別的切換(非同步IO)。注意:asyncio只能發tcp級別的請求,不能發http協議。

非同步IO:所謂「非同步 IO」,就是你發起一個 網路IO 操作,卻不用等它結束,你可以繼續做其他事情,當它結束時,你會得到通知。

實現方式:單執行緒+協程實現非同步IO操作。

非同步協程用法

接下來讓我們來了解下協程的實現,從 Python 3.4 開始,Python 中加入了協程的概念,但這個版本的協程還是以生成器物件為基礎的,在 Python 3.5 則增加了 async/await,使得協程的實現更加方便。首先我們需要了解下面幾個概念:

  • event_loop:事件迴圈,相當於一個無限迴圈,我們可以把一些函式註冊到這個事件迴圈上,當滿足條件發生的時候,就會呼叫對應的處理方法。
  • coroutine:中文翻譯叫協程,在 Python 中常指代為協程物件型別,我們可以將協程物件註冊到時間迴圈中,它會被事件迴圈呼叫。我們可以使用 async 關鍵字來定義一個方法,這個方法在呼叫時不會立即被執行,而是返回一個協程物件。
  • task:任務,它是對協程物件的進一步封裝,包含了任務的各個狀態。
  • future:代表將來執行或沒有執行的任務的結果,實際上和 task 沒有本質區別。

另外我們還需要了解 async/await 關鍵字,它是從 Python 3.5 才出現的,專門用於定義協程。其中,async 定義一個協程,await 用來掛起阻塞方法的執行。

1.定義一個協程

示例:

from time import sleep
import asyncio

async def request(url):
  print('正在請求url')
  sleep(2)
  print('下載成功')

# 返回一個特殊的協程物件,request函式內部不會被執行
c = request('www.baidu.com')

# 例項化一個事件迴圈物件
loop = asyncio.get_event_loop()

# 基於事件迴圈物件建立一個任務物件,並將協程物件封裝到該物件中
task = loop.create_task(c)

# 另一種形式例項化任務物件的方法
task = asyncio.ensure_future(c)

# 將協程物件註冊到事件迴圈物件中,並需要啟動事件迴圈物件
# 當事件迴圈物件內的第一個引數遇到阻塞是,就會自動執行後面的物件,當第一個物件的阻塞結束是會上報給事件迴圈物件,然後事件迴圈物件繼續執行第一個物件,從而達到非同步的效果
loop.run_until_complete(task)

2.給任務物件繫結回撥

import asyncio

async def request(url):
  print('正在請求url')
  print('下載成功')
  return url

# 回撥函式必須有一個引數:task【任務物件】
# task.result():任務物件中封裝的協程物件對應的特殊函式內部的返回值
def callback(task):
  print('this is callback')
  print(task.result())

c = request('www.baidu.com')
# 建立一個任務物件
task = asyncio.ensure_future(c)
# 給任務物件繫結一個回撥函式
task.add_done_callback(callback)
# 例項化一個事件迴圈物件
loop = asyncio.get_event_loop()
# 將協程物件註冊到事件迴圈物件中,並需要啟動事件迴圈物件
loop.run_until_complete(task)

3.多工非同步協程

import asyncio
import time

urls = ['www.baidu.com','www.sogou,com','www.goubanjia.com']
start_time = time.time()
async def request(url):
  print('正在請求url')
  # 在多工非同步協程事項中,不可以出現不支援非同步的相關程式碼,sleep不支援
  # sleep(2)
  await asyncio.sleep(2)
  print('下載成功')

loop = asyncio.get_event_loop()
# 任務列表:防止多個任務物件
tasks = []
for url in urls:
  c = request(url)
  task = asyncio.ensure_future(c)
  tasks.append(task)

loop.run_until_complete(asyncio.wait(tasks))
print(time.time() - start_time)

4.多非同步任務協程應用

# aiohttp:支援非同步的一個基於網路請求的模組
import aiohttp
import asyncio
import time

urls = ['http://127.0.0.1:5000/jay','http://127.0.0.1:5000/bobo','http://127.0.0.1:5000/tom',]

start_time = time.time()

async def get_pageText(url):
  async with aiohttp.ClientSession() as s:# 例項化請求物件
    async with await s.get(url) as response:
      page_text = await response.text()
      print(page_text)
      # 這裡有返回值,是因為要用回撥函式進行資料解析
      return page_text

# 封裝回調函式用於資料解析
def parse(task):
  # 1.獲取相應資料
  page_text = task.reault()
  print(page_text+',即將進行資料解析...')
  # 以下解析操作


tasks = []
for url in urls:
  c = get_pageText(url)
  task = asyncio.ensure_future(c)
  # 給任務物件繫結回撥函式用於資料解析
  task.add_done_callback(parse)
  tasks.append(task)

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
print(time.time() - start_time)

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。