日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

python2异步编程_最新Python异步编程详解

發(fā)布時(shí)間:2025/3/19 python 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python2异步编程_最新Python异步编程详解 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

我們都知道對(duì)于I/O相關(guān)的程序來(lái)說(shuō),異步編程可以大幅度的提高系統(tǒng)的吞吐量,因?yàn)樵谀硞€(gè)I/O操作的讀寫(xiě)過(guò)程中,系統(tǒng)可以先去處理其它的操作(通常是其它的I/O操作),那么Python中是如何實(shí)現(xiàn)異步編程的呢?

簡(jiǎn)單的回答是Python通過(guò)協(xié)程(coroutine)來(lái)實(shí)現(xiàn)異步編程。那究竟啥是協(xié)程呢?這將是一個(gè)很長(zhǎng)的故事。

故事要從yield開(kāi)始說(shuō)起(已經(jīng)熟悉yield的讀者可以跳過(guò)這一節(jié))。

yield

yield是用來(lái)生成一個(gè)生成器的(Generator), 生成器又是什么呢?這又是一個(gè)長(zhǎng)長(zhǎng)的story,所以這次我建議您移步到這里:

完全理解Python迭代對(duì)象、迭代器、生成器,而關(guān)于yield是怎么回事,建議看這里:[翻譯]PYTHON中YIELD的解釋

好了,現(xiàn)在假設(shè)你已經(jīng)明白了yield和generator的概念了,請(qǐng)?jiān)徫疫@種不負(fù)責(zé)任的說(shuō)法但是這真的是一個(gè)很長(zhǎng)的story啊!

總的來(lái)說(shuō),yield相當(dāng)于return,它將相應(yīng)的值返回給調(diào)用next()或者send()的調(diào)用者,從而交出了cpu使用權(quán),而當(dāng)調(diào)用者再調(diào)用next()或者send()時(shí),又會(huì)返回到y(tǒng)ield中斷的地方,如果send有參數(shù),又會(huì)將參數(shù)返回給yield賦值的變量,如果沒(méi)有就跟next()一樣賦值為None。但是這里會(huì)遇到一個(gè)問(wèn)題,就是嵌套使用generator時(shí)外層的generator需要寫(xiě)大量代碼,看如下示例:

注意以下代碼均在Python3.6上運(yùn)行調(diào)試

#!/usr/bin/env python

# encoding:utf-8

def inner_generator():

i = 0

while True:

i = yield i

if i > 10:

raise StopIteration

def outer_generator():

print("do something before yield")

from_inner = 0

from_outer = 1

g = inner_generator()

g.send(None)

while 1:

try:

from_inner = g.send(from_outer)

from_outer = yield from_inner

except StopIteration:

break

def main():

g = outer_generator()

g.send(None)

i = 0

while 1:

try:

i = g.send(i + 1)

print(i)

except StopIteration:

break

if __name__ == '__main__':

main()

為了簡(jiǎn)化,在Python3.3中引入了yield from

yield from

使用yield from有兩個(gè)好處,

可以將main中send的參數(shù)一直返回給最里層的generator,

同時(shí)我們也不需要再使用while循環(huán)和send (), next()來(lái)進(jìn)行迭代。

我們可以將上邊的代碼修改如下:

def inner_generator():

i = 0

while True:

i = yield i

if i > 10:

raise StopIteration

def outer_generator():

print("do something before coroutine start")

yield from inner_generator()

def main():

g = outer_generator()

g.send(None)

i = 0

while 1:

try:

i = g.send(i + 1)

print(i)

except StopIteration:

break

if __name__ == '__main__':

main()

執(zhí)行結(jié)果如下:

do something before coroutine start

1

2

3

4

5

6

7

8

9

10

這里inner_generator()中執(zhí)行的代碼片段我們實(shí)際就可以認(rèn)為是協(xié)程,所以總的來(lái)說(shuō)邏輯圖如下:

coroutine and wrapper

接下來(lái)我們就看下究竟協(xié)程是啥樣子

協(xié)程coroutine

協(xié)程的概念應(yīng)該是從進(jìn)程和線程演變而來(lái)的,他們都是獨(dú)立的執(zhí)行一段代碼,但是不同是線程比進(jìn)程要輕量級(jí),協(xié)程比線程還要輕量級(jí)。多線程在同一個(gè)進(jìn)程中執(zhí)行,而協(xié)程通常也是在一個(gè)線程當(dāng)中執(zhí)行。它們的關(guān)系圖如下:

process, thread and coroutine

我們都知道Python由于GIL(Global Interpreter Lock)原因,其線程效率并不高,并且在*nix系統(tǒng)中,創(chuàng)建線程的開(kāi)銷(xiāo)并不比進(jìn)程小,因此在并發(fā)操作時(shí),多線程的效率還是受到了很大制約的。所以后來(lái)人們發(fā)現(xiàn)通過(guò)yield來(lái)中斷代碼片段的執(zhí)行,同時(shí)交出了cpu的使用權(quán),于是協(xié)程的概念產(chǎn)生了。在Python3.4正式引入了協(xié)程的概念,代碼示例如下:

import asyncio

# Borrowed from http://curio.readthedocs.org/en/latest/tutorial.html.

@asyncio.coroutine

def countdown(number, n):

while n > 0:

print('T-minus', n, '({})'.format(number))

yield from asyncio.sleep(1)

n -= 1

loop = asyncio.get_event_loop()

tasks = [

asyncio.ensure_future(countdown("A", 2)),

asyncio.ensure_future(countdown("B", 3))]

loop.run_until_complete(asyncio.wait(tasks))

loop.close()

示例顯示了在Python3.4引入兩個(gè)重要概念協(xié)程和事件循環(huán),

通過(guò)修飾符@asyncio.coroutine定義了一個(gè)協(xié)程,而通過(guò)event loop來(lái)執(zhí)行tasks中所有的協(xié)程任務(wù)。之后在Python3.5引入了新的async & await語(yǔ)法,從而有了原生協(xié)程的概念。

async & await

在Python3.5中,引入了aync&await 語(yǔ)法結(jié)構(gòu),通過(guò)"aync def"可以定義一個(gè)協(xié)程代碼片段,作用類(lèi)似于Python3.4中的@asyncio.coroutine修飾符,而await則相當(dāng)于"yield from"。

先來(lái)看一段代碼,這個(gè)是我剛開(kāi)始使用async&await語(yǔ)法時(shí),寫(xiě)的一段小程序。

#!/usr/bin/env python

# encoding:utf-8

import asyncio

import requests

import time

async def wait_download(url):

response = await requests.get(url)

print("get {} response complete.".format(url))

async def main():

start = time.time()

await asyncio.wait([

wait_download("http://www.163.com"),

wait_download("http://www.mi.com"),

wait_download("http://www.google.com")])

end = time.time()

print("Complete in {} seconds".format(end - start))

loop = asyncio.get_event_loop()

loop.run_until_complete(main())

這里會(huì)收到這樣的報(bào)錯(cuò):

Task exception was never retrieved

future: exception=TypeError("object Response can't be used in 'await' expression",)>

Traceback (most recent call last):

File "asynctest.py", line 10, in wait_download

data = await requests.get(url)

TypeError: object Response can't be used in 'await' expression

這是由于requests.get()函數(shù)返回的Response對(duì)象不能用于await表達(dá)式,可是如果不能用于await,還怎么樣來(lái)實(shí)現(xiàn)異步呢?

原來(lái)Python的await表達(dá)式是類(lèi)似于"yield from"的東西,但是await會(huì)去做參數(shù)檢查,它要求await表達(dá)式中的對(duì)象必須是awaitable的,那啥是awaitable呢? awaitable對(duì)象必須滿(mǎn)足如下條件中其中之一:

A native coroutine object returned from a native coroutine function .

原生協(xié)程對(duì)象

A generator-based coroutine object returned from a function decorated with types.coroutine() .

types.coroutine()修飾的基于生成器的協(xié)程對(duì)象,注意不是Python3.4中asyncio.coroutine

An object with an await method returning an iterator.

實(shí)現(xiàn)了await method,并在其中返回了iterator的對(duì)象

根據(jù)這些條件定義,我們可以修改代碼如下:

#!/usr/bin/env python

# encoding:utf-8

import asyncio

import requests

import time

async def download(url): # 通過(guò)async def定義的函數(shù)是原生的協(xié)程對(duì)象

print("get %s" % url)

response = requests.get(url)

print(response.status_code)

async def wait_download(url):

await download(url) # 這里download(url)就是一個(gè)原生的協(xié)程對(duì)象

print("get {} data complete.".format(url))

async def main():

start = time.time()

await asyncio.wait([

wait_download("http://www.163.com"),

wait_download("http://www.mi.com"),

wait_download("http://www.baidu.com")])

end = time.time()

print("Complete in {} seconds".format(end - start))

loop = asyncio.get_event_loop()

loop.run_until_complete(main())

至此,程序可以運(yùn)行,不過(guò)仍然有一個(gè)問(wèn)題就是它并沒(méi)有真正地異步執(zhí)行 (這里要感謝網(wǎng)友荊棘花王朝,是Ta指出的這個(gè)問(wèn)題)

看一下運(yùn)行結(jié)果:

get http://www.163.com

200

get http://www.163.com data complete.

get http://www.baidu.com

200

get http://www.baidu.com data complete.

get http://www.mi.com

200

get http://www.mi.com data complete.

Complete in 0.49027466773986816 seconds

會(huì)發(fā)現(xiàn)程序始終是同步執(zhí)行的,這就說(shuō)明僅僅是把涉及I/O操作的代碼封裝到async當(dāng)中是不能實(shí)現(xiàn)異步執(zhí)行的。必須使用支持異步操作的非阻塞代碼才能實(shí)現(xiàn)真正的異步。目前支持非阻塞異步I/O的庫(kù)是aiohttp

#!/usr/bin/env python

# encoding:utf-8

import asyncio

import aiohttp

import time

async def download(url): # 通過(guò)async def定義的函數(shù)是原生的協(xié)程對(duì)象

print("get: %s" % url)

async with aiohttp.ClientSession() as session:

async with session.get(url) as resp:

print(resp.status)

# response = await resp.read()

# 此處的封裝不再需要

# async def wait_download(url):

# await download(url) # 這里download(url)就是一個(gè)原生的協(xié)程對(duì)象

# print("get {} data complete.".format(url))

async def main():

start = time.time()

await asyncio.wait([

download("http://www.163.com"),

download("http://www.mi.com"),

download("http://www.baidu.com")])

end = time.time()

print("Complete in {} seconds".format(end - start))

loop = asyncio.get_event_loop()

loop.run_until_complete(main())

再看一下測(cè)試結(jié)果:

get: http://www.mi.com

get: http://www.163.com

get: http://www.baidu.com

200

200

200

Complete in 0.27292490005493164 seconds

可以看出這次是真正的異步了。

好了現(xiàn)在一個(gè)真正的實(shí)現(xiàn)了異步編程的小程序終于誕生了。

而目前更牛逼的異步是使用uvloop或者pyuv,這兩個(gè)最新的Python庫(kù)都是libuv實(shí)現(xiàn)的,可以提供更加高效的event loop。

uvloop和pyuv

關(guān)于uvloop可以參考uvloop

pyuv可以參考這里pyuv

pyuv實(shí)現(xiàn)了Python2.x和3.x,但是該項(xiàng)目在github上已經(jīng)許久沒(méi)有更新了,不知道是否還有人在維護(hù)。

uvloop只實(shí)現(xiàn)了3.x, 但是該項(xiàng)目在github上始終活躍。

它們的使用也非常簡(jiǎn)單,以u(píng)vloop為例,只需要添加以下代碼就可以了

import asyncio

import uvloop

asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

關(guān)于Python異步編程到這里就告一段落了,而引出這篇文章的引子實(shí)際是關(guān)于網(wǎng)上有關(guān)Sanic和uvloop的組合創(chuàng)造的驚人的性能,感興趣的同學(xué)可以找下相關(guān)文章,也許后續(xù)我會(huì)再專(zhuān)門(mén)就此話題寫(xiě)一篇文章,歡迎交流!

總結(jié)

以上是生活随笔為你收集整理的python2异步编程_最新Python异步编程详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。