并发编程与异步IO-Python
并发编程与异步IO
基本概念:并发,并行,同步,异步,阻塞,非阻塞
并发:一个时间段内,有几个程序在同一个 CPU 上运行,但是任意时刻只有一个程序在 CPU 上运行。
并行:在任意时刻点上,有多个程序同时运行在**多个 CPU **上。如果 CPU 有个四颗,那么并行最多只有四个。
基于以上,我们都说高并发,不说高并行。
同步:代码调用 IO 操作时,必须等待 IO 操作完成才返回的调用方式。
异步:指代码调用 IO 操作时,不必等 IO 操作完成就返回的调用方式。
阻塞:指调用函数时,当前线程被挂起。
非阻塞:指调用函数时,当前线程不会被挂起,而是立即返回。
阻塞和非阻塞是说的函数调用的一种机制。
多线程与多进程
CPython中,多线程thread
常用来处理io密集型的操作,因为GIL的原因无法利用多核优势,如果是计算密集型的操作需要多进程multiprocess
,多线程的优势在与线程之间数据共享,也真是因为数据共享所以多线程会抢占共享资源这就需要锁这种东西,多进程不会出现这种情况因为数据不共享嘛(相当于多个python解释器在运行),但是这也有问题,多进程通信就是一个难题.
线程模块:Threading
进程模块:Multiprocessing
懒人包:Concurrent
异步框架 asyncio
异步框架的发展由来: yield-->yield from -->gevent/greenlet-->asyncio,asyncio
是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。
基本概念:
event_loop 事件循环
:程序开启一个无限的循环,程序员会把一些函数(协程)注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。coroutine 协程
:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。future 对象
: 代表将来执行或没有执行的任务的结果。它和task上没有本质的区别task 任务
:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含任务的各种状态。Task 对象是 Future 的子类,它将 coroutine 和 Future 联系在一起,将 coroutine 封装成一个 Future 对象。async/await 关键字
:python3.5 用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。其作用在一定程度上类似于yield。
asyncio协程是如何工作的
- 1.创建协程对象coroutine,创建事件循环event_loop
- 2.将协程转为task任务,之后可以选择添加回调函数
- 3.将task任务扔进事件循环对象中触发
- 4.获取任务结果
单任务携程
单任务的携程是没有什么意义的这里主要是为了了解asyncio协程整体是如何工作的
import asyncio
import time
#1创建事件循环event_loop
loop = asyncio.get_event_loop()
#2创建携程函数
async def func1():
await asyncio.sleep(1)
return '1finished'
async def func2():
await asyncio.sleep(3)
return '2finished'
#3 携程函数转成携程对象
coroutine = func1()
#4 携程对象转成task对象
task = loop.create_task(coroutine)
#5 给task添加回调函数
def _callback(task):
print(f'回调函数获得结果:{task.result()}')
task.add_done_callback(_callback)
#6 启动事件循环
loop.run_until_complete(task)
#7获得任务结果
print(task.result())
多任务携程并发实现
1可以用另一个函数把携程函数整合起来,并发的逻辑在整合函数中实现,但是如果方法不对可能无法实现并发
import asyncio
import time
#1创建事件循环event_loop
loop = asyncio.get_event_loop()
#2创建携程函数
async def func1():
await asyncio.sleep(1)
return '1finished'
async def func2():
await asyncio.sleep(3)
return '2finished'
#不能并发的的例子
async def test_error():
star = time.time()
coroutine1 = func1()
coroutine2 = func2()
await loop.create_task(coroutine1)
await loop.create_task(coroutine2)
print('all done',time.time()-star)
if __name__ == '__main__':
loop.run_until_complete(test_error())
#这样写就可以并发了
async def test1():
star = time.time()
coroutine1 = func1()
coroutine2 = func2()
task1 = loop.create_task(coroutine1)
task2 = loop.create_task(coroutine2)
await task1
await task2
print('all done',time.time()-star)
if __name__ == '__main__':
loop.run_until_complete(test1())
#通常并发的用法,使用asyncio.wait或者asyncio.gather
async def test2():
star = time.time()
coroutine1 = func1()
coroutine2 = func2()
task1 = loop.create_task(coroutine1)
task2 = loop.create_task(coroutine2)
tasks = [task1,task2]
result = await asyncio.gather(*tasks)
print(task1.result())
print('all done',time.time()-star)
if __name__ == '__main__':
loop.run_until_complete(test2())
async def test3():
star = time.time()
coroutine1 = func1()
coroutine2 = func2()
task1 = loop.create_task(coroutine1)
task2 = loop.create_task(coroutine2)
tasks = [task1,task2]
await asyncio.wait(tasks)
print(task1.result())
print('all done',time.time()-star)
if __name__ == '__main__':
loop.run_until_complete(test3())
#多任务携程回调函数的使用,依次给task添加回调函数
def callback(task):
print(f'回调函数触发:{task.result()}')
async def test4():
star = time.time()
coroutine1 = func1()
coroutine2 = func2()
task1 = loop.create_task(coroutine1)
task2 = loop.create_task(coroutine2)
tasks = [task1,task2]
for task in tasks:
task.add_done_callback(callback)
await asyncio.gather(*tasks)
print('all done',time.time()-star)
if __name__ == '__main__':
loop.run_until_complete(test3())
创建task对象的3种方法,三种方法都可,看个人习惯
loop.create_task
asyncio.create_task
asyncio.ensure_future