python随用随学20200220-异步IO
啥是异步IO
众所周知,CPU速度太快,磁盘,网络等IO跟不上. 而程序一旦遇到IO的时候,就需要等IO完成才能进行才能进行下一步的操作. 严重拖累了程序速度.
因为一个IO操作就阻塞了当前线程,导致其他代码无法执行,所以我们必须使用多线程或者多进程来并发的执行代码.
但是多线程或者多进程虽然解决了并发问题. 但是线程的增加,系统切换线程的开销也会变大. 如果线程太多,CPU的时间就花在了频繁切换线程上.(为啥会有开销,如果不懂的话,请看计算机专业本科教材,操作系统原理)
所以除了多线程和多进程之外,还有一个办法就是异步IO. 也就是传说中的消息订阅机制.
进程发出IO请求后就不管了,然后去干其他的事儿. 等IO返回消息之后再去处理.
如果采用异步IO的话,我们平常的这种顺序执行的代码就不好使了,需要有一个一直在监听事件的消息循环. 一般情况下,我们会使用一个无限循环来进行监听. 是的你没有看错,就是个死循环.
在异步模式下,没有发生频繁的线程切换. 对于IO密集型的场景,异步IO非常合适.
协程
协程,又叫微线程,coroutine.函数在所有语言中都是层级调用,比如A中调用了B,B中又调用了C. 那么C执行完返回,B执行完返回,A执行完.结束.
所以函数的调用都是通过栈来实现的.一个线程执行的就是一个函数.
函数的调用都会有一个入口和一个返回. 所以函数调用的顺序都是明确的. 而协程的调用和函数的调用完全不同.
协程可以在执行的时候中断,然后再在合适的时候返回来接着执行.
协程从执行结果上来看,有点像多线程,但是其优点在于没有发生线程的切换,所以其效率是远高于多线程的. 而且不需要锁...
python中的协程是通过generator来实现的. Python中的yeild不但可以返回一个值,还可以接收调用者发出的参数.
- def consumer():
- r = ''
- while True:
- n = yield r
- if not n:
- return
- print('[CONSUMER] Consuming %s...' % n)
- r = '200 ok'
- def produce(c):
- c.send(None)
- n = 0
- while n < 5:
- n = n + 1
- print('[PRODUCER] Producing %s' % n)
- r = c.send(n)
- print('[PRODUCER] Consumer return %s' % r)
- c.close()
- c = consumer()
- produce(c)
廖雪峰的代码,应该很好看懂. 不多做解释了.
协程的使用
asyncio
是python3.4之后的版本引入的标准库,内置了对异步IO的支持.
asyncio就是一个消息循环模型. 我们从asyncio模块中获取一个eventloop然后把协程丢到这个eventloop中,就可以实现异步IO
- import asyncio
- @asyncio.coroutine
- def hello():
- print('hello world')
- r = yield from asyncio.sleep(2)
- print('hello world again!')
- loop = asyncio.get_event_loop()
- loop.run_until_complete(hello())
- loop.close()
- import threading
- import asyncio
- @asyncio.coroutine
- def hello():
- print('hello world! (%s)' % threading.current_thread())
- yield from asyncio.sleep(2)
- print('hello world again (%s)' % threading.current_thread())
- loop = asyncio.get_event_loop()
- task = [hello(), hello()]
- loop.run_until_complete(asyncio.wait(task))
- loop.close()
这里就可以看出来,两个hello()函数是由同一个线程并发执行的.
coroutine asyncio.wait(aws, *, loop=None, timeout=None, return_when=ALL_COMPLETED)
Run awaitable objects in the aws set concurrently and block until the condition specified by return_when.
If any awaitable in aws is a coroutine, it is automatically scheduled as a Task. Passing coroutines objects to wait() directly is deprecated as it leads to confusing behavior.
这是里关于wait方法的定义. 说实话没搞太懂...
async和await
构造协程并使用asyncio的两种方法. 第一种就是上面说说的. 使用修饰符@asyncio.coroutine
并在协程内使用yield from
另外在python3.5版本后新增了async
和await
关键字. 使用也很简单,用async
替代修饰符@asyncio.coroutine
使用await
替换yield from
coroutine asyncio.wait(aws, *, loop=None, timeout=None, return_when=ALL_COMPLETED)
Run awaitable objects in the aws set concurrently and block until the condition specified by return_when.Returns two sets of Tasks/Futures: (done, pending).
另外这个方法在python3.8之后被deprecated了.
定义一个协程
- import asyncio
- import time
- async def so_some_work(x):
- print('waiting: ',x)
- now = lambda :time.time()
- start = now()
- coroutine = so_some_work(2)
- loop = asyncio.get_event_loop()
- loop.run_until_complete(coroutine)
- print('TIME: ',now() - start)
这是协程的定义方法,注意协程并不是可以直接执行的函数. 这里通过get_event_loop()
方法获取一个一个事件循环,然后通过run_until_complete()
方法讲协程注册到消息循环中去,并启动消息循环.
创建一个task
Tasks are used to schedule coroutines concurrently.
When a coroutine is wrapped into a Task with functions likeasyncio.create_task()
the coroutine is automatically scheduled to run soon
task是Future的子类,保存了协程运行后的状态,用于未来获取协程结果.
协程是不能直接运行的,在使用方法run_until_complete()
方法注册协程的时候,协程就自动被包装成了一个task对象.
- import asyncio
- import time
- async def do_some_work(x):
- print('Waiting: ', x)
- now = lambda: time.time()
- start = now()
- coroutine = do_some_work(2)
- loop = asyncio.get_event_loop()
- task = loop.create_task(coroutine)
- print(task)
- loop.run_until_complete(task)
- print(task)
- print('TINE: ', now() - start)
比如这个例子就创建了一个task.
从这个结果来看,task在加入时间循环之前是pending的状态. 而运行完之后 是done的状态,而且还有有个result参数.
绑定回调
绑定回调,在task执行完毕之后可以获取执行的结果,也就是协程得返回值. 回调的最后一个参数是future对象,如果回调需要多个参数,也可以通过偏函数导入.
单个参数的callback
- import asyncio
- import time
- async def so_some_work(x):
- print('Waitting: ', x)
- return 'Done after {0}s'.format(x)
- def callback(future):
- print('Callback: ', future.result())
- now = lambda: time.time()
- start = now()
- coroutine = so_some_work(2)
- loop = asyncio.get_event_loop()
- task = asyncio.ensure_future(coroutine)
- task.add_done_callback(callback)
- loop.run_until_complete(task)
- print('TIME: ', now() - start)
ensure_future()
和create_task()
都可以创建一个task. 不过create_task()
是python3.7之后新增的方法.
有多个参数的callback
- def callback2(t, future):
- print('Callback: ', t, future.result())
- --------------------------------------------------
- task.add_done_callback(functools.partial(callback2, 2))
回调让程序逻辑变的更加复杂(就是容易写着写着就懵逼了). 其实在task执行完毕之后,可以直接使用result()方法获取其返回值. 这样就不需要回调了.
- import asyncio
- import time
- now = lambda :time.time()
- async def do_some_work(x):
- print('Waitting: ',x)
- return 'Done after {}s'.format(x)
- start = now()
- coroutine = do_some_work(2)
- loop = asyncio.get_event_loop()
- task = asyncio.ensure_future(coroutine)
- loop.run_until_complete(task)
- print('Task ret: {}'.format(task.result()))
- print('TIME: {}'.format(now()-start))
await和阻塞
async
可以用来定义协程对象, await
可以针对耗时的操作进行挂起,就像yield
一样可以把协程暂时挂起,让出控制权.
协程遇到await,时间循环会把当前协程暂时挂起,执行别的协程,知道其他的协程也挂起或者执行完毕,再进行下一个协程的执行
针对一些耗时的IO操作一般会是用await的方式挂起它.
- import asyncio
- import time
- now = lambda :time.time()
- async def do_some_work(x):
- print('Waitting: ',x)
- await asyncio.sleep(x)
- return 'Done after {}s'.format(x)
- start = now()
- coroutine = do_some_work(2)
- loop = asyncio.get_event_loop()
- task = asyncio.ensure_future(coroutine)
- loop.run_until_complete(task)
- print('Task ret: {}'.format(task.result()))
- print('TIME: {}'.format(now()-start))
一个协程看不出啥区别. 多整几个来看看
- import asyncio
- import time
- now = lambda :time.time()
- async def do_some_work(x):
- print('Waitting: ',x)
- await asyncio.sleep(x)
- return 'Done after {}s'.format(x)
- start = now()
- coroutine1 = do_some_work(1)
- coroutine2 = do_some_work(2)
- coroutine3 = do_some_work(4)
- loop = asyncio.get_event_loop()
- tasks=[
- asyncio.ensure_future(coroutine1),
- asyncio.ensure_future(coroutine2),
- asyncio.ensure_future(coroutine3)
- ]
- # loop.run_until_complete(asyncio.wait(tasks))
- loop.run_until_complete(asyncio.wait(tasks))
- for task in tasks:
- print('Task ret: ',task.result())
- print('TIME: {}'.format(now()-start))
其实我想说到这里,我才真正理解wait方法是干啥的...执行一个task或者协程的集合...
这里还要多说一点就是并行和并发的区别.
并行的意思是同一时刻有多个任务在执行.
并发的意思是有多个任务要同时进行.
按照我的理解,并发就是TDD,并行就是FDD
协程的嵌套(这特么是有病吧...)
不是有病...是为了实现更多的IO操作过程,也就是一个协程中await了另外一个协程
- import asyncio
- import time
- now = lambda: time.time()
- async def do_some_work(x):
- print('Waitting: ', x)
- await asyncio.sleep(x)
- return 'Done after {}s'.format(x)
- async def foo():
- coroutine1 = do_some_work(1)
- coroutine2 = do_some_work(2)
- coroutine3 = do_some_work(4)
- tasks = [
- asyncio.ensure_future(coroutine1),
- asyncio.ensure_future(coroutine2),
- asyncio.ensure_future(coroutine3)
- ]
- dones, pendings = await asyncio.wait(tasks)
- for task in dones:
- print('Task ret: ', task.result())
- start = now()
- loop = asyncio.get_event_loop()
- loop.run_until_complete(foo())
- print('TIME: ', now() - start)
协程的停止
future有几个状态:
- Pending: task刚创建的时候
- Running: 时间循环调用执行的时候
- Done: 执行完成的时候
- Cancelled: 被cancel()之后
- import asyncio
- import time
- now = lambda :time.time()
- async def do_some_work(x):
- print('Waitting: ',x)
- await asyncio.sleep(x)
- print('Done after {}s'.format(x))
- corutine1 = do_some_work(1)
- corutine2 = do_some_work(2)
- corutine3 = do_some_work(4)
- tasks = [
- asyncio.ensure_future(corutine1),
- asyncio.ensure_future(corutine2),
- asyncio.ensure_future(corutine3)
- ]
- start = now()
- loop = asyncio.get_event_loop()
- try:
- loop.run_until_complete(asyncio.wait(tasks))
- except KeyboardInterrupt as e:
- print(asyncio.Task.all_tasks())
- for task in asyncio.Task.all_tasks():
- print(task.cancel())
- loop.stop()
- loop.run_forever()
- finally:
- loop.close()
- print('TIME: ', now()-start)
20200222-实在写不下去了,以后再补吧...
参考资料:
python随用随学20200220-异步IO的更多相关文章
- 第十一章:Python高级编程-协程和异步IO
第十一章:Python高级编程-协程和异步IO Python3高级核心技术97讲 笔记 目录 第十一章:Python高级编程-协程和异步IO 11.1 并发.并行.同步.异步.阻塞.非阻塞 11.2 ...
- 【Python学习之九】asyncio—异步IO
asyncio 这是python3.4引入的标准库,直接内置对异步IO的支持.asyncio的编程模型就是一个消息循环.从asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程 ...
- python中同步、多线程、异步IO、多线程对IO密集型的影响
目录 1.常见并发类型 2.同步版本 3.多线程 4.异步IO 5.多进程 6.总结 1.常见并发类型 I/ O密集型: 蓝色框表示程序执行工作的时间,红色框表示等待I/O操作完成的时间.此图没有按比 ...
- python 学习笔记九 队列,异步IO
queue (队列) 队列是为线程安全使用的. 1.先入先出 import queue #测试定义类传入队列 class Foo(object): def __init__(self,n): self ...
- python网络编程-Select\Poll\Epoll异步IO
首先列一下,sellect.poll.epoll三者的区别 select select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select ...
- 【Python之路】特别篇--事件驱动与异步IO
通常,我们写服务器处理模型的程序时,有以下几种模型: (1)每收到一个请求,创建一个新的进程,来处理该请求: (2)每收到一个请求,创建一个新的线程,来处理该请求: (3)每收到一个请求,放入一个事件 ...
- IO模型--阻塞IO,非阻塞IO,IO多路复用,异步IO
IO模型介绍: * blocking IO 阻塞IO * nonblocking IO 非阻塞IO * IO multiplexing IO多路复用 * signal driven IO 信号驱动IO ...
- 多线程,多进程和异步IO
1.多线程网络IO请求: #!/usr/bin/python #coding:utf-8 from concurrent.futures import ThreadPoolExecutor impor ...
- python学记笔记 2 异步IO
在IO编程中,我们知道CPU的速度远远快于磁盘,网络IO,在一个线程中,CPU执行速度的代码非常快,然而遇到IO操作就需要阻塞 需要等待IO操作完成才能继续下一步的动作.这种情况叫做同步IO 在IO操 ...
随机推荐
- 造轮子-toast组件的实现(下)
1.解决 toast 中传入 html 的问题,通过假的 slot 来实现 // plugins.js toast.$slots.default = [message] // toast.vue &l ...
- emeditor安装及插件信息
原文地址:https://www.52pojie.cn/thread-658917-1-1.html 废话不多说 官网:https://www.emeditor.com/download/ 安装版:6 ...
- 从DirectX SDK升级到Windows SDK
原来的DirectX SDK到June 2010,微软就不更新了.之后新的版本被集成到了Windows SDK中. 在微软的博客里找到一篇升级指南:http://blogs.msdn.com/b/ch ...
- Irrelevant Elements UVA-1635 (二项式定理)
vjudge链接 原题链接 乍一看似乎没什么思路,但是写几个简单的例子之后规律就变得很明显. 比如当 n=5 时,每一步计算后的结果如下: a1 a1+a2 a1+2a2+a3 a1+3a2+3a3+ ...
- CSS-12-盒子模型
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- ios--->ios沙盒总结
ios沙盒总结 沙盒介绍 iOS应用程序只能在该程序创建的文件系统中读取文件,不可以去其它地方访问,此区域被成为沙盒,所以所有的非代码文件都要保存在此,例如图像,图标,声音,映像,属性列表,文本文件等 ...
- React报错Failed prop type: Invalid prop `component` of type `object` supplied to `Route`, expected `function`
引言 最近在忙毕业设计,博客也很久没更新了,毕业设计使用vue做了一个校园寻物网站,现在开始学Raect,记录一下自己遇到问题,react-redux的connect方法使得组件与Redux建立了联系 ...
- javase第一章(了解java)
------------恢复内容开始------------ java介绍 java这门语言,如果你是一名IT从业者,那么就一定是会有所耳闻的,毕竟,这是编程史上其商业化最成功的一门语言,当然, 编程 ...
- 安卓开发实战-记账本APP(六)
记账本APP开发---终结篇 昨天的动态数字录屏奉上:在抖音上拍了一个(ps:欢迎点赞) https://v.douyin.com/poEjmG/ 今天将图表的内容进行了制作,我用的是MPChart的 ...
- WTL Hello World
构建最简单的WTL Hello World程序,基于:WTL91_5321_Final + VS2013 + WIN7 添加->新建项目 为了简单起见,我们删除一些button和对应的处理代码( ...