关于协程锁
尽管asyncio
库是使用单线程来实现协程的,但是它还是并发的,乱序执行的。可以说是单线程的调度系统,并且由于执行有延时或者I/O中断等因素,每个协程如果同步时,还是得使用一些同步对象来实现。
在asyncio
库中定义了一个锁对象Lock
,它一次只允许一个协程来访问共享的资源,如果多协程访问就会阻塞起来,也就是说如果一个协程没有释放这个锁,别的协程是没有办法访问共享的资源的。
遇到的一个问题
Python 协程在遇到 await 时会显式切换,这种切换类似于 GIL 锁的切换,如果是非原子性操作,就会出现数据或操作顺序与预期不一致的现象。
1 | In [5]: import asyncio |
预期是 100,实际是 1。因为协程 1 读到的 b 为 0,遇到 await 切换到 f(),再切回到协程 2,读到的 b 为 0,遇到 await 切换到 f(),再切回到协程 3,读到的 b 为 0,当第 100 个协程切回到 f(),最后切回协程 99,此时 b+1 得到 1,再切回协程 98,此时 b+1 得到 1,最终 a[0]=1。
解决问题
加锁可以将并发变成串行
1 | In [1]: import asyncio |
使用场景
对协程的执行顺序有要求,但执行代码非原子操作,且被await
语句隔开。或者是对某个协程进行保护在多次调用的时候只有第一次调用成功,其他阻塞等待。适用于分配资源的场景,防止资源被重新分配。
源码
我们看下源码中的使用注释
1 | Usage: |
再跑个例子
1 | import asyncio |
参考文章: