1. 想学asyncio,得先了解协程
携程的意义:
2.协程和多线程之间的共同点和区别:
共同点:
都是并发操作,多线程同一时间点只能有一个线程在执行,协程同一时间点只能有一个任务在执行;
不同点:
多线程,是在I/O阻塞时通过切换线程来达到并发的效果,在什么情况下做线程切换是由操作系统来决定的,开发者不用操心,但会造成竞争条件 (race condition) ;
协程,只有一个线程,在I/O阻塞时通过在线程内切换任务来达到并发的效果,在什么情况下做任务切换是开发者决定的,不会有竞争条件 (race condition) 的情况;多线程的线程切换比协程的任务切换开销更大;
对于开发者而言,多线程并发的代码比协程并发的更容易书写。
一般情况下协程并发的处理效率比多线程并发更高。
3. greenlet实现协程
greenlet用于创建协程,switch用于进行协程之间的切换某个协程在执行的过程中可以随时的被其他协程通过switch函数来打断,转而去执行其他协程,当前协程的中断现场会被保留,一旦中断的协程再次获得cpu的执行权首先会恢复现场然后从中断处继续执行这种机制下的协程是同步,不能并发
pip install greenlet
import timeimport greenlet def func1(): print("func11") gr2.switch() time.sleep(1) print("func22") gr2.switch() def func2(): print("func33") gr1.switch() time.sleep(1) print("func44") start = time.time()gr1 = greenlet.greenlet(func1)gr2 = greenlet.greenlet(func2)gr1.switch()end = time.time()print(end - start)4. yield关键字实现协程
def func1(): yield 1 yield from func2() yield 3 def func2(): yield 2 yield 4 ff = func1()for item in ff: print(item)5.gevent协程
(1)gevent实现协程
pip install gevent
from greenlet import greenletfrom time import sleepdef func1(): print("协程1") sleep(2) g2.switch() print("协程1恢复运行") def func2(): print("协程2") sleep(1) g3.switch()def func3(): print("协程3") sleep(1) g1.switch() if __name__ == '__main__': # 使用greenlet来创建三个协程 g1 = greenlet(func1) g2 = greenlet(func2) g3 = greenlet(func3) # print(g1) g1.switch() # 让协程g1取抢占cpu资源(2) gevent实现异步协程
# 协程被创建出来以后默认是多个协程同步执行# 我们可以加入monkey补丁,把同步的协程转成异步协程from gevent import monkey # 注意:monkey的引入必须在其他模块之前 monkey.patch_all() # 用monkey给整个协程队列,添加一个非阻塞I/O的补丁,使得他们成为异步协程import timeimport requestsimport gevent headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'} def func(url, i): print("协程%d开启!" % i) res = requests.get(url=url, headers=headers) html = res.text print("协程%d执行结束,获取到的响应体大小为:%d" % (i, len(html))) if __name__ == '__main__': start = time.time() urls = [ "https://plete,一个简便的写法。# asyncio.run 函数在 Python 3.7 中加入 asyncio 模块,asyncio.run(result)这个过程可以简单理解为:将协程当做任务添加到 事件循环 的任务列表,然后事件循环检测列表中的协程是否 已准备就绪(默认可理解为就绪状态),如果准备就绪则执行其内部代码。
7.4 await关键字
await是一个只能在协程函数中使用的关键字,用于遇到IO操作时挂起 当前协程(任务),当前协程(任务)挂起过程中 事件循环可以去执行其他的协程(任务),当前协程IO处理完成时,可以再次切换回来执行await之后的代码,
await + 可等待对象(协程对象、Future对象、Task对象)
示例1:await+协程对象
import asyncio async def func1(): print("start") await asyncio.sleep(1) print("end") return "func1执行完毕" async def func2(): print("func2开始执行") # await关键字后面可以跟可等待对象(协程对象、Future对象、Task对象) response = await func1() print(response) print("func2执行完毕") asyncio.run(func2())示例2: 协程函数中可以使用多次await关键字
import asyncio async def func1(): print("start") await asyncio.sleep(1) print("end") return "func1执行完毕" async def func2(): print("func2开始执行") # await关键字后面可以跟可等待对象(协程对象、Future对象、Task对象) response = await func1() print(response) response2 = await func1() print(response2) print("func2执行完毕") asyncio.run(func2())7.5 task对象
Tasks用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入事件循环中等待被调度执行。除了使用 asyncio.create_task() 函数以外,还可以用低层级的 loop.create_task() 或 ensure_future() 函数。不建议手动实例化 Task 对象。
本质上是将协程对象封装成task对象,并将协程立即加入事件循环,同时追踪协程的状态。
注意:asyncio.create_task() 函数在 Python 3.7 中被加入。在 Python 3.7 之前,可以改用低层级的 asyncio.ensure_future() 函数。
示例1:
import asyncio async def func(): print(1) await asyncio.sleep(1) print(2) return "func的返回值" async def main(): print(3) # 创建协程,将协程封装到一个task对象中并立即添加到事件循环列表中,等待事件循环去执行,(默认是就绪状态) task1 = asyncio.create_task(func()) # 创建协程,将协程封装到一个task对象中并立即添加到事件循环列表中,等待事件循环去执行,(默认是就绪状态) task2 = asyncio.create_task(func()) # 当执行某协程遇到IO操作时,会自动化切换执行其他任务。 # 此处的await是等待相对应的协程全都执行完毕并获取结果 ret1 = await task1 ret2 = await task2 print(ret1, ret2) asyncio.run(main())示例2:用的还是比较多的
import asyncio async def func(): print(1) await asyncio.sleep(1) print(2) return "func的返回值" async def main(): print(3) # 创建协程,将协程封装到Task对象中并添加到事件循环的任务列表中,等待事件循环去执行(默认是就绪状态)。 # 在调用 task_list = [ asyncio.create_task(func()), asyncio.create_task(func()) ] # 当执行某协程遇到IO操作时,会自动化切换执行其他任务。 # 此处的await是等待所有协程执行完毕,并将所有协程的返回值保存到done # 如果设置了timeout值,则意味着此处最多等待的秒,完成的协程返回值写入到done中,未完成则写到pending中。 done, pending = await asyncio.wait(task_list, timeout=None) print(done) print(pending) asyncio.run(main())示例3:
import asyncio async def func(): print("执行协程函数内部代码") # 遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行。当前协程挂起时,事件循环可以去执行其他协程(任务)。 response = await asyncio.sleep(2) print("IO请求结束,结果为:", response) coroutine_list = [func(), func()]# 错误:coroutine_list = [ asyncio.create_task(func()), asyncio.create_task(func()) ]# 此处不能直接 asyncio.create_task,因为将Task立即加入到事件循环的任务列表,# 但此时事件循环还未创建,所以会报错。# 使用asyncio.wait将列表封装为一个协程,并调用asyncio.run实现执行两个协程# asyncio.wait内部会对列表中的每个协程执行ensure_future,封装为Task对象。done, pending = asyncio.run(asyncio.wait(coroutine_list))总结:
在程序中只要看到async和await关键字,其内部就是基于协程实现的异步编程,这种异步编程是通过一个线程在IO等待时间去执行其他任务,从而实现并发。
如果是 I/O 密集型,且 I/O 请求比较耗时的话,使用协程。
如果是 I/O 密集型,且 I/O 请求比较快的话,使用多线程。
如果是 计算 密集型,考虑可以使用多核 CPU,使用多进程。
以上就是python中asyncio异步编程学习的详细内容,更多关于python中使用asyncio的资料请关注其它相关文章!