Python -- asyncio库

前言

  • 3.4版本加入标准库。
    asyncio底层基于selectors实现,看似库,其实就是个框架,包含异步IO、事件循环、协程、任务等内容

问题的引出

def a():
    for x in range(3):
        print(x)

def b():
    for x in 'abc':
        print(x)

a()
b()
# 执行结果
0
1
2
a
b
c

这是一个串行的程序,单线程中根本没有做任何
如何并行?

多线程版本

import threading
import time

def a():
    for x in range(3):
        time.sleep(0.001)
        print(x)

def b():
    for x in 'abc':
        time.sleep(0.001)
        print(x)

t1 = threading.Thread(target=a, name='a')
t2 = threading.Thread(target=b, name='b')
t1.start()
t2.start()

# 执行结果
0
1
a
2
b
c

多进程版本

import multiprocessing
import time

def a():
    for x in range(3):
        time.sleep(0.001)
        print(x)

def b():
    for x in 'abc':
        time.sleep(0.001)
        print(x)

if __name__ == '__main__':
    t1 = multiprocessing.Process(target=a, name='a')
    t2 = multiprocessing.Process(target=b, name='b')
    t1.start()
    t2.start()

# 执行结果
a
b
c
0
1
2

生成器版本

def a():
    for x in range(3):
        print(x)
        yield

def b():
    for x in 'abc':
        print(x)
        yield

x = a()
y = b()
for i in range(3):
    next(x)
    next(y)

# 执行结果
0
a
1
b
2
c

上例在一个线程内通过生成器完成了调度,让两个函数都有机会执行,这样的调度不是操作系统的进程、线程完成的,而是用户自己设计出来的。
这个程序编写:

  1. 需要使用yield来让出控制权
  2. 需要循环帮助交替执行

事件循环

事件循环asyncio提供的核心运行机制

columncolumn
asyncio.get_event_loop()返回一个事件循环对象,是asyncio.BaseEventLoop的实例
AbstractEventLoop.stop()停止运行事件循环
AbstractEventLoop.run_forever()一直运行,直到stop()
AbstractEventLoop.run_until_complete(future)运行直至Future对象运行完,返回Future的结果。参数可以是Future类或子类Task的对象。如果是协程对象也会被封装成Task对象
AbstractEventLoop.is_running()返回事件循环的是否运行
AbstractEventLoop.close()关闭事件循环
AbstractEventLoop.create_task(coro使用协程对象创建任务对象

协程

  • 协程不是进程、也不是线程,它是用户空间调度的完成并发处理的方式
  • 进程、线程由操作系统完成调度,而协程是线程内完成调度。它不需要更多的线程,自然也没有多线程切换带来的开销
  • 协程是非抢占式调度,只有一个协程主动让出控制权,另一个协程才会被调度
  • 协程也不需要使用锁机制,因为是在同一个线程中执行
    -** 多CPU下**,可以使用多进程和协程配合,既能进程并发又能发挥协程在单线程中的优势
  • Python中协程是基于生成器
  • asyncio.iscoroutine(obj) 判断是不是协程对象
  • asyncio.iscoroutinefunction(func) 判断是不是协程函数

Future

  • concurrent.futures.Future类似。通过Future对象可以了解任务执行的状态数据
  • 事件循环来监控Future对象是否完成

Task任务

Task类是Future子类,它的作用就是把协程包装成一个Future对象

协程的使用

3.4引入asyncio,使用装饰器,将生成器函数转换成协程函数,就可以在事件循环中执行了

import asyncio

@asyncio.coroutine
def sleep(x):
    for i in range(3): # 协程函数
        print('sleep {}'.format(i))
        yield from asyncio.sleep(x)
    return "result = {}".format(1000)

# 事件循环
loop = asyncio.get_event_loop()

# 本质就是tasks的ensure_future,把协程包装进一个Future对象中,并使用create_task返回一个task
future = asyncio.ensure_future(sleep(3))

# 内部会调用ensure_future,内部会执行loop.run_forever()
loop.run_until_complete(future)

print('-' * 30)
loop.close()
print(future.result()) # 拿return值
print('===end===')

# 执行结果
sleep 0
sleep 1
sleep 2
------------------------------
result = 1000
===end===

ensure_future(coro_or_future):

  • 如果参数已经是future直接返回
  • 如果是协程,则使用loop.create_task()创建task,并返回task

可以使用create_task得到task对象

import asyncio

@asyncio.coroutine
def sleep(x): # 协程函数
    for i in range(3):
        print('sleep {}'.format(i))
        yield from asyncio.sleep(x)
    return "result={}".format(1000)

# 时间循环
loop = asyncio.get_event_loop()

# create_task返回一个task
task = loop.create_task(sleep(3))
print(1, task) # pending
# 内部会调用ensure_future,内部会执行loop.run_forever()
loop.run_until_complete(task)
print(2, task) # finished

print('-' * 30)
loop.close()
print(task.result()) # 拿return值
print('===end===')

# 执行结果
1 <Task pending coro=<sleep() running at C:/Users/Administrator/PycharmProjects/test/Task/Temporary.py:3>>
sleep 0
sleep 1
sleep 2
2 <Task finished coro=<sleep() done, defined at C:/Users/Administrator/PycharmProjects/test/Task/Temporary.py:3> result='result=1000'>
------------------------------
result=1000
===end===

回调的使用

上面的代码还可以协程

loop = asyncio.get_event_loop()

# 本质就是tasks的ensure_future,把协程包装进一个Future对象中,并返回一个task
# 内部会调用ensure_future,内部会执行loop.fun_forever()
loop.fun_until_complete(sleep(3)) # 包装协程为task

print('-' * 30)
loop.close()
  • 上例中,似乎拿不到sleep(3)这个协程对象的结果future了,run_until_complete函数的返回值就是其参数future对象的返回结果
    -future对象都可以调用add_done_callback(fn)增加回调函数,回调函数是单参的,参数就是future对象

代码如下:

import asyncio

@asyncio.coroutine
def sleep(x): # 协程函数
    for i in range(3):
        print('sleep {}'.format(i))
        yield from asyncio.sleep(x)
    return "result={}".format(1000)

# 回调函数,参数必须是future
def cb(future):
    print('in callback. future = {}'.format(future))
    print(future.result())

# 事件循环
loop = asyncio.get_event_loop()

# create_task返回一个task
task = loop.create_task(sleep(3))
task.add_done_callback(cb) # 增加回调
print(1, task) # pending
# 内部会调用ensure_future, 内部会执行loop.run_forever()
loop.run_until_complete(task)
print(2, task) # finished

print('-' * 30)
loop.close()
print(task.result()) # 拿return值
print('===end===')

# 执行结果
1 <Task pending coro=<sleep() running at C:/Users/Administrator/PycharmProjects/test/Task/Temporary.py:3> cb=[cb() at C:/Users/Administrator/PycharmProjects/test/Task/Temporary.py:11]>
sleep 0
sleep 1
sleep 2
in callback. future = <Task finished coro=<sleep() done, defined at C:/Users/Administrator/PycharmProjects/test/Task/Temporary.py:3> result='result=1000'>
result=1000
2 <Task finished coro=<sleep() done, defined at C:/Users/Administrator/PycharmProjects/test/Task/Temporary.py:3> result='result=1000'>
------------------------------
result=1000
===end===

多个任务执行

import asyncio

@asyncio.coroutine
def a():
    for x in range(3):
        print('a.x', x)
        yield
    return 'a.finished'

@asyncio.coroutine
def b():
    for x in range(3):
        print('b.x', x)
        yield
    return 'b.finished'

print(asyncio.iscoroutinefunction(a), asyncio.iscoroutinefunction(b))

t1 = a()
t2 = b()
print(asyncio.iscoroutine(t1), asyncio.iscoroutine(t2))

# 事件大循环
loop = asyncio.get_event_loop()
tasks = [t1, t2]
# asyncio.wait 会迭代列表中的对象并封装成{f1,f2},返回一个协程对象f
# 循环执行f,它内部等价yield from {f1,f2}
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

# 执行结果
True True
True True
b.x 0
a.x 0
b.x 1
a.x 1
b.x 2
a.x 2

使用回调,如下

import asyncio

@asyncio.coroutine
def a():
    for x in range(3):
        print('a.x', x)
        yield
    return 'a.finished'

@asyncio.coroutine
def b():
    for x in range(3):
        print('b.x', x)
        yield
    return 'b.finished'

print(asyncio.iscoroutinefunction(a), asyncio.iscoroutinefunction(b))


# 事件大循环
loop = asyncio.get_event_loop()


fs = set()
for t in (a(), b()):
    f = asyncio.ensure_future(t)
    f.add_done_callback(lambda f: print(f.result())) # 单个完成就调用回调
    fs.add(f)

# asyncio.wait 会迭代列表中的对象并封装成{f1,f2},返回一个协程对象f
# 循环执行f,它内部等价yield from {f1,f2}
results = loop.run_until_complete(asyncio.wait(fs))
loop.close()

print(results) # 全部结果,全部完成才能看

# 执行结果
True True
a.x 0
b.x 0
a.x 1
b.x 1
a.x 2
b.x 2
a.finished
b.finished
({<Task finished coro=<a() done, \
defined at C:/Users/Administrator/PycharmProjects/test/Task/Temporary.py:3> result='a.finished'>, \
<Task finished coro=<b() done, \
defined at C:/Users/Administrator/PycharmProjects/test/Task/Temporary.py:10> result='b.finished'>}, set())

注意:

run_until_complete方法的返回结果,必须所有任务执行完才能看

新语法

3.5版本开始,Python提供关键字async、await,在语言上原生支持协程

import asyncio

async def sleep(x):
    for i in range(3):
        print('sleep {}'.format(i))
        await asyncio.sleep(x)

loop = asyncio.get_event_loop()
loop.run_until_complete(sleep(3))
loop.close()

# 执行结果
sleep 0
sleep 1
sleep 2
  • async def用来定义协程函数,iscoroutinefunction()返回True
  • 协程函数中可以不包含awaitasync关键字,但不能使用yield关键字
  • 如同生成器函数调用返回生成器对象一样,协程函数调用也会返回一个对象称为协程对象,iscoroutine()返回True

await语句之后是awaitable对象,可以是协程或者实现了__await__() 方法的对象。await暂停当前协程执行使loop调度其他协程

import asyncio

@asyncio.coroutine
def w():
    yield

async def a():
    for x in range(3):
        print('a.x', x)
        await w()
    return 'a.finished'

async def b():
    for x in range(3):
        print('b.x', x)
        await w()
    return 'b.finished'

print(asyncio.iscoroutinefunction(a), asyncio.iscoroutinefunction(b))

t1 = a()
t2 = b()
print(asyncio.iscoroutine(t1), asyncio.iscoroutine(t2))

def cb(future):
    print(future.result())

# 事件大循环
loop = asyncio.get_event_loop()
tasks = [t1, t2]
fs = set()
for t in tasks:
    f = asyncio.ensure_future(t)
    f.add_done_callback(cb)
    fs.add(f)

# asyncio.wait 会迭代列表中的对象并封装成{f1,f2}, 返回一个协程对象f
# 循环执行f,它内部等价yield from {f1,f2}
results = loop.run_until_complete(asyncio.wait(fs))
loop.close()

print(results) # 全部结果,全部完成才能看

# 执行结果
True True
True True
a.x 0
b.x 0
a.x 1
b.x 1
a.x 2
b.x 2
a.finished
b.finished
({<Task finished coro=<a() done, \
defined at C:/Users/Administrator/PycharmProjects/test/Task/Temporary.py:7> result='a.finished'>,\
 <Task finished coro=<b() done, \
 defined at C:/Users/Administrator/PycharmProjects/test/Task/Temporary.py:13> result='b.finished'>}, set())

TCP Echo Server举例

import asyncio

# 处理的回调
from asyncio.streams import StreamReader, StreamWriter
async def handle(reader:StreamReader, writre:StreamWriter):
    while True: # 和socketserver类似,一个连接一个handle
        data = await reader.read(1024)
        if not data or data == b'quit':
            print('quit~~~')
            break
        print(type(reader), type(writre))
        client = writre.get_extra_info('peername')
        message = "{} Your msg {}".format(client, data.decode()).encode()
        writre.write(message)
        await writre.drain() # 注意不是flush方法
    writre.close()

loop = asyncio.get_event_loop()
ip = '127.0.0.1'
port = 9999

# 返回一个协程对象
coro = asyncio.start_server(handle, ip, port, loop=loop)
print(asyncio.iscoroutine(coro), coro)
server = loop.run_until_complete(coro) # 返回server
print(server)

try:
    loop.run_forever()
except KeyboardInterrupt:
    pass
finally:
    server.close()
    loop.run_until_complete(server.wait_closed())
    loop.close()

aiohttp库

异步HTTP客户端、服务端框架

安装

$ pip install aiohttp

文档

https://aiohttp.readthedocs.io/en/stable/

开发

  • HTTP Server
from aiohttp import web

async def indexhandle(request:web.Request):
    return web.Response(text=request.path, status=201)

async def handle(request:web.Request):
    print(request.match_info)
    print(request.query_string) # http://127.0.0.1:8080/1?name=12301
    return web.Response(test=request.match_info.get('id', '0000'), status=200)

app = web.Application()
app.router.add_get("/", indexhandle) # http://127.0.0.1:8080/
app.router.add_get("/{id}", handle) # http://127.0.0.1:8080/12301
# app.add_routes([
#     web.get('/', indexhandle),
#     web.get('/{id}', handle),
# ])
web.run_app(app, host='0.0.0.0', port=9977)

HTTP Client

import asyncio
from aiohttp import ClientSession

async def get_html(url:str):
    async with ClientSession() as session:
        async with session.get(url) as res:
            print(res.status)
            print(await res.text())

url = 'http://127.0.0.1:8080/2?age=20&name=jerry'

loop = asyncio.get_event_loop()
loop.run_until_complete(get_html(url))
loop.close()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值