asyncio 基础用法

时间:2021-10-13 17:11:58

asyncio 基础用法

  • python也是在python 3.4中引入了协程的概念。也通过这次整理更加深刻理解这个模块的使用

asyncio 是干什么的?

  • asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。

  • 异步网络操作

  • 并发

  • 协程

asyncio的一些关键字:

  • event_loop 事件循环:程序开启一个无限循环,把一些函数注册到事件循环上,当满足事件发生的时候,调用相应的协程函数
  • **coroutine **协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。
  • **task **任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含了任务的各种状态
  • future: 代表将来执行或没有执行的任务的结果。它和task上没有本质上的区别
  • async/await 关键字:python3.5用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。

Python 3.4 asyncio 用法

import asyncio
import threading @asyncio.coroutine
def hello():
print("Hello world!", threading.currentThread())
# 异步调用asyncio.sleep(1):
r = yield from asyncio.sleep(2)
# time.sleep(2)
print("Hello world!", threading.currentThread()) # 获取EventLoop:
loop = asyncio.get_event_loop()
# 执行coroutine
tasks = [hello(), hello()]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
  • Python 3.5 定义了 async/await 直接替换 @asyncio.coroutine 和 yield from

基础用法:

import asyncio
import time now = lambda: time.time() async def do_some_work(x):
print(f"协程执行: {x}")
await asyncio.sleep(x) return "done after {}".format(x) def callback(future):
print("回调执行获取返回值: ", future.result()) start = now()
# 这里是一个协程对象,这个时候do_some_work函数并没有执行
coroutine = do_some_work(2)
# print(coroutine) # <coroutine object do_some_work at 0x000001A2AAA9FCA8>
loop = asyncio.get_event_loop() # 创建一个 task 对象
# task = loop.create_task(coroutine)
# 第二种创建方式 通过 asyncio 创建
task = asyncio.ensure_future(coroutine) # 绑定回调,在task执行完成的时候可以获取执行的结果,回调的最后一个参数是future对象,通过该对象可以获取协程返回值。
task.add_done_callback(callback)
print("未执行的task pending 状态: ", task)
loop.run_until_complete(task)
print("执行完的task finished 状态: ", task) print("执行时间: ", now() - start)
  • 执行效果
未执行的task pending 状态:  <Task pending coro=<do_some_work() running at F:/爬虫/爬虫项目使用 pycharm/PyppeteerDemo/asyncioDemo.py:7> cb=[callback() at F:/爬虫/爬虫项目使用 pycharm/PyppeteerDemo/asyncioDemo.py:14]>

协程执行: 2

回调执行获取返回值:  done after 2

执行完的task finished 状态:  <Task finished coro=<do_some_work() done, defined at F:/爬虫/爬虫项目使用 pycharm/PyppeteerDemo/asyncioDemo.py:7> result='done after 2'>

执行时间:  2.0016515254974365

并发和并行

  • 并发指的是同时具有多个活动的系统

    并行值得是用并发来使一个系统运行的更快。并行可以在操作系统的多个抽象层次进行运用

    所以并发通常是指有多个任务需要同时进行,并行则是同一个时刻有多个任务执行

用了aysncio实现了并发

import asyncio
import time now = lambda: time.time() async def do_some_work(x):
print("协程执行", x)
await asyncio.sleep(x)
return f"done after {x}" start = now() coroutine1 = do_some_work(2)
coroutine2 = do_some_work(3)
coroutine3 = do_some_work(4) tasks = [
asyncio.ensure_future(coroutine1),
asyncio.ensure_future(coroutine2),
asyncio.ensure_future(coroutine3),
] loop = asyncio.get_event_loop()
# 执行所有的 task 接收 一个 task 列表
# loop.run_until_complete(asyncio.wait(tasks))
# 第二种写法 接收 一堆 task
loop.run_until_complete(asyncio.gather(*tasks)) for item in tasks:
print(item.result())
print("执行时间: ", now() - start)
  • 执行效果
协程执行 2
协程执行 3
协程执行 4
done after 2
done after 3
done after 4
执行时间: 4.00330114364624

协程嵌套

  • 封装更多的io操作过程,即一个协程中await了另外一个协程,连接起来。这样就实现了嵌套的协程.
import asyncio
import time now = lambda: time.time() start = now() async def do_some_work(x):
print("协程执行: ", x)
await asyncio.sleep(x)
return f"done after {x}" async def main():
coroutine1 = do_some_work(2)
coroutine2 = do_some_work(3)
coroutine3 = do_some_work(4) tasks = [
asyncio.ensure_future(coroutine1),
asyncio.ensure_future(coroutine2),
asyncio.ensure_future(coroutine3),
]
# 第一种
# dones 完成的 task对象 pendings 等待的 task
# dones, pendings = await asyncio.wait(tasks)
# print(pendings)
# for task in dones:
# print(task.result())
# 或者直接返回
# return await asyncio.wait(tasks) # 第二种
# 使用 asyncio.gather 直接得到结果列表
# results = await asyncio.gather(*tasks)
# print(results)
# for result in results:
# print(result)
# 或者直接返回
# return results # 第三种
# asyncio.as_completed(tasks) 是一个生成器
# print( asyncio.as_completed(tasks)) # <generator object as_completed at 0x000001F0DB4767D8> for task in asyncio.as_completed(tasks):
result = await task
print(task) # <generator object as_completed.<locals>._wait_for_one at 0x000001557DA46830>
print(result) # done after 2 loop = asyncio.get_event_loop() # 第一种 返回值
# dines, pendings = loop.run_until_complete(main())
# print(pendings)
# for task in dines:
# print(task.result()) # 第二种返回值
# results = loop.run_until_complete(main())
# for result in results:
# print("返回的内容 : ", result) # 第三种
loop.run_until_complete(main()) print("执行时间: ", now() - start)

协程的停止

  • future对象有几个状态:

    • Pending
    • Running
    • Done
    • Cacelled

    创建future的时候,task为pending,事件循环调用执行的时候当然就是running,调用完毕自然就是done,如果需要停止事件循环,就需要先把task取消。可以使用asyncio.Task获取事件循环的task.

import asyncio
import time now = lambda: time.time() start = now() async def do_some_work(x):
print("协程执行: {}".format(x))
await asyncio.sleep(x)
return "done after {}".format(x) coroutine1 = do_some_work(2)
coroutine2 = do_some_work(3)
coroutine3 = do_some_work(4) tasks = [
asyncio.ensure_future(coroutine1),
asyncio.ensure_future(coroutine2),
asyncio.ensure_future(coroutine3),
] loop = asyncio.get_event_loop() try:
results = loop.run_until_complete(asyncio.wait(tasks))
except KeyboardInterrupt as k:
# print(k) print(asyncio.Task.all_tasks())
for task in asyncio.Task.all_tasks():
print(task.cancel()) # 循环task,逐个cancel
loop.stop() # stop之后还需要再次开启事件循环
loop.run_forever() finally:
loop.close() # 最后在close,不然还会抛出异常 print(now() - start)
  • 执行结果 : 使用 命令窗口执行 Ctrl + c 会抛出 run_until_complete 的 KeyboardInterrupt 异常
协程执行: 2
协程执行: 3
协程执行: 4 {<Task pending coro=<do_some_work() running at asyncioDemo.py:158> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x0000025F84BF3DF8>()]> cb=[_wait.<locals>._on_comp
letion() at c:\python36\Lib\asyncio\tasks.py:380]>, <Task pending coro=<do_some_work() running at asyncioDemo.py:158> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at
0x0000025F84B64C18>()]> cb=[_wait.<locals>._on_completion() at c:\python36\Lib\asyncio\tasks.py:380]>, <Task pending coro=<wait() running at c:\python36\Lib\asyncio\tasks.py:313> w
ait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x0000025F84BF3F18>()]>>, <Task pending coro=<do_some_work() running at asyncioDemo.py:158> wait_for=<Future pending cb
=[<TaskWakeupMethWrapper object at 0x0000025F84BF3D98>()]> cb=[_wait.<locals>._on_completion() at c:\python36\Lib\asyncio\tasks.py:380]>} True
True
True
True 2.00264573097229

不同线程的事件循环

  • 我们的事件循环用于注册协程,而有的协程需要动态的添加到事件循环中。一个简单的方式就是使用多线程。当前线程创建一个事件循环,然后在新建一个线程,在新线程中启动事件循环。当前线程不会被block。

import asyncio
from threading import Thread
import time now = lambda: time.time() def start_loop(loop):
asyncio.set_event_loop(loop)
loop.run_forever() async def do_some_work(x):
print('Waiting {}'.format(x))
await asyncio.sleep(x)
print('Done after {}s'.format(x)) def work(x):
print("开始", x)
time.sleep(x)
print("结束", x) start = now() new_loop = asyncio.new_event_loop() t = Thread(target=start_loop, args=(new_loop,))
t.start() new_loop.call_soon_threadsafe(work, 6)
new_loop.call_soon_threadsafe(work, 3)
print(now() - start)
"""
开始 6
0.002008199691772461
结束 6
开始 3
结束 3 """
'''
启动上述代码之后,当前线程不会被block,新线程中会按照顺序执行call_soon_threadsafe方法注册的more_work方法, 后者因为time.sleep操作是同步阻塞的,因此运行完毕more_work需要大致6 + 3
'''
#
# asyncio.run_coroutine_threadsafe(do_some_work(6), new_loop)
# asyncio.run_coroutine_threadsafe(do_some_work(3), new_loop)
# print(now() - start)
"""
Waiting 6
Waiting 3
0.0009968280792236328
Done after 3s
Done after 6s """
'''
上述的例子,主线程中创建一个new_loop,然后在另外的子线程中开启一个无限事件循环。 主线程通过run_coroutine_threadsafe新注册协程对象。这样就能在子线程中进行事件循环的并发操作,同时主线程又不会被block。一共执行的时间大概在6s左右。
'''
  • 参考 廖雪峰 文档
async def wget(host):
print('wget %s...' % host) reader, writer = await asyncio.open_connection(host, 80) header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % host
writer.write(header.encode('utf-8'))
await writer.drain()
while True:
line = await reader.readline()
if line == b'\r\n':
break
print('%s header > %s' % (host, line.decode('utf-8').rstrip()))
# Ignore the body, close the socket
writer.close() loop = asyncio.get_event_loop()
tasks = [wget(host) for host in ['www.sina.com.cn', 'www.sohu.com', 'www.163.com']]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
wget www.sina.com.cn...
wget www.sohu.com...
wget www.163.com...
www.sina.com.cn header > HTTP/1.1 302 Moved Temporarily
www.sina.com.cn header > Server: nginx
www.sina.com.cn header > Date: Sat, 27 Apr 2019 14:14:29 GMT
www.sina.com.cn header > Content-Type: text/html
www.sina.com.cn header > Content-Length: 154
www.sina.com.cn header > Connection: close
www.sina.com.cn header > Location: https://www.sina.com.cn/
www.sina.com.cn header > X-Via-CDN: f=edge,s=cmcc.hebei.ha2ts4.140.nb.sinaedge.com,c=183.197.88.253;
www.sina.com.cn header > X-Via-Edge: 1556374469876fd58c5b798403e6f6e7f94d2
www.sohu.com header > HTTP/1.1 200 OK
www.sohu.com header > Content-Type: text/html;charset=UTF-8
www.sohu.com header > Connection: close
www.sohu.com header > Server: nginx
www.sohu.com header > Date: Sat, 27 Apr 2019 14:13:44 GMT
www.sohu.com header > Cache-Control: max-age=60
www.sohu.com header > X-From-Sohu: X-SRC-Cached
www.sohu.com header > Content-Encoding: gzip
www.sohu.com header > FSS-Cache: HIT from 4742539.7953813.5615036
www.sohu.com header > FSS-Proxy: Powered by 3628410.5725572.4500890
www.163.com header > HTTP/1.0 302 Moved Temporarily
www.163.com header > Server: Cdn Cache Server V2.0
www.163.com header > Date: Sat, 27 Apr 2019 14:14:29 GMT
www.163.com header > Content-Length: 0
www.163.com header > Location: http://www.163.com/special/0077jt/error_isp.html
www.163.com header > X-Via: 1.0 xiyidong136:1 (Cdn Cache Server V2.0)
www.163.com header > Connection: close

asyncio 基础用法的更多相关文章

  1. PropertyGrid控件由浅入深&lpar;二&rpar;:基础用法

    目录 PropertyGrid控件由浅入深(一):文章大纲 PropertyGrid控件由浅入深(二):基础用法 控件的外观构成 控件的外观构成如下图所示: PropertyGrid控件包含以下几个要 ...

  2. logstash安装与基础用法

    若是搭建elk,建议先安装好elasticsearch 来自官网,版本为2.3 wget -c https://download.elastic.co/logstash/logstash/packag ...

  3. elasticsearch安装与基础用法

    来自官网,版本为2.3 注意elasticsearch依赖jdk,2.3依赖jdk7 下载rpm包并安装 wget -c https://download.elastic.co/elasticsear ...

  4. BigDecimal最基础用法

    BigDecimal最基础用法 用字符串生成的BigDecimal是不会丢精度的. 简单除法. public class DemoBigDecimal { public static void mai ...

  5. Vue组件基础用法

    前面的话 组件(Component)是Vue.js最强大的功能之一.组件可以扩展HTML元素,封装可重用的代码.根据项目需求,抽象出一些组件,每个组件里包含了展现.功能和样式.每个页面,根据自己所需, ...

  6. Smarty基础用法

    一.Smarty基础用法: 1.基础用法如下 include './smarty/Smarty.class.php';//引入smarty类 $smarty = new Smarty();//实例化s ...

  7. 前端自动化测试神器-Katalon的基础用法

    前言 最近由于在工作中需要通过Web端的功能进行一次大批量的操作,数据量大概在5000左右,如果手动处理, 完成一条数据的操作用时在20秒左右的话,大概需要4-5个人/天的工作量(假设一天8小时的工作 ...

  8. Bootstrap fileinput:文件上传插件的基础用法

    官网地址:http://plugins.krajee.com/ 官网提供的样例:http://plugins.krajee.com/file-input/demo 基础用法一 导入核心CSS及JS文件 ...

  9. oracle入坑日记&lt&semi;六&gt&semi;自增列创建和清除(含序列和触发器的基础用法)

    0   前言 用过 SQLserver 和 MySQL 的自增列(auto_increment),然而 Oracle 在建表设置列时却没有自增列. 查阅资料后发现 Oracle 的自增列需要手动编写. ...

随机推荐

  1. ReactNative入门(安卓)——API(下)

    LayoutAnimation - layout动画 当布局发生改变时的动画模块,它有两个方法: 1. 最常用的方法是 LayoutAnimation.configureNext(conf<Ob ...

  2. Python 3 利用 subprocess 实现管道&lpar; pipe &rpar;交互操作读&sol;写通信

    这里我们用Windows下的shell来举例: from subprocess import * #因为是举例,就全部导入了 为了方便你理解,我们用一个很简单的一段代码来说明: 可以看见我们利用Pop ...

  3. python发布文件&lpar;windows&rpar;

    怎样发布文件 首先发布本地文件有一个好的用处,就是省去了朋友同import的时候还要使用sys.path,省的自己出错 1.新建文件夹d:\ tool 在的d:\tool文件夹中建立login.py ...

  4. PHP数组的操作

    一.数组操作的基本函数数组的键名和值array_values($arr);获得数组的值array_keys($arr);获得数组的键名array_flip($arr);数组中的值与键名互换(如果有重复 ...

  5. java Graphics2D 画图

    在Java中,当需要画一些特殊的形状时,比如说椭圆.矩形等,可以使用 Graphics2D 来绘图. 一些API: g.drawLine(3,3,50,50);//画一条线段 g.drawRect(8 ...

  6. 【MVC】过滤器

    APS.NET MVC中(以下简称“MVC”)的每一个请求,都会分配给相应的控制器和对应的行为方法去处理,而在这些处理的前前后后如果想再加一些额外的逻辑处理.这时候就用到了过滤器. MVC支持的过滤器 ...

  7. WINDOWS7&comma;8和os x yosemite 10&period;10&period;1懒人版双系统安装教程

    安装过程 磁盘划分 懒人版如果不是整盘单系统或者双硬盘双系统安装我们需要在当前系统磁盘划分两块磁盘空间,一个用来做安装盘,一个作为系统盘. 我这里是单硬盘,想从最后一个盘符压缩出80GB的空来安装黑苹 ...

  8. &lbrack;ABP&rsqb;浅谈模块系统与 ABP 框架初始化

    在 ABP 框架当中所有库以及项目都是以模块的形式存在,所有模块都是继承自AbpModule 这个抽象基类,每个模块都拥有四个生命周期.分别是: PreInitialze(); Initialize( ...

  9. Springboot&plus; mybatis&plus; mysql配置&commat;Slf4j

    spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver # 驱动 name: testDB # 配置名,可以随便写 userna ...

  10. Hadoop集群datanode磁盘不均衡的解决方案【转】

    一.引言: Hadoop的HDFS集群非常容易出现机器与机器之间磁盘利用率不平衡的情况,比如集群中添加新的数据节点,节点与节点之间磁盘大小不一样等等.当hdfs出现不平衡状况的时候,将引发很多问题,比 ...