协程封装问题 这个差异疯了, 跪求大佬解答
下面是2个不同的封装方式, 但是线程的使用却有明显的不同,一个是可以乱序的,一个是按顺序执行的,求大神解答。 不知道原因。import asyncio
import time
async def ceshi01(x,y):
await asyncio.sleep(y)
print(f"我是{x},我运行了{y}分钟")
async def yunxing():
group_a = asyncio.gather(ceshi01('A',5),ceshi01('a',3))#线程a
group_b = asyncio.gather(ceshi01('b',4),ceshi01('B',2))#线程b
await group_a
await group_b
print(asyncio.run(yunxing()))
运行结果是: 结果是按时间排序的, 效率最高。
我是B,我运行了2分钟
我是a,我运行了3分钟
我是b,我运行了4分钟
我是A,我运行了5分钟
另外一个封装方式
import asyncio
import time
async def ceshi01(x,y):
await asyncio.sleep(y)
print(f"我是{x},我运行了{y}分钟")
async def yunxing():
awaitasyncio.gather(ceshi01('A',5),ceshi01('a',3))
awaitasyncio.gather(ceshi01('b',4),ceshi01('B',2))
awaitasyncio.gather(ceshi01('c',1),ceshi01('c',1))
print(asyncio.run(yunxing()))
运行结果是: 这里的结果是按顺序执行的 从a到c
我是a,我运行了3分钟
我是A,我运行了5分钟
我是B,我运行了2分钟
我是b,我运行了4分钟
我是c,我运行了1分钟
我是c,我运行了1分钟 基本上大部分程序语言都是单一线程的,意思就是先做完一个事情,然后才做另外一个事情,这是肯定的。
Python 附有 asyncio 模组里的 async、await 用于解决同步和异步问题,简单的说就是当你执行一个函数时,系统不会等待返回结果,直接运行下一个函数(看似双线程,其实是单线程)
用容易理解方式解说:async 就好象告诉系统,我要同时执行多个函数,不用等到结束函数时才执行另外一个。而 await asyncio.sleep() 如同 time.sleep(),唯一差别就是 await 用于提醒系统,还有多久记得回来看这个函数,不然函数不会结束(RuntimeWarning: coroutine 'xxx' was never awaited)
我的解说肯定没有网路上的好,你也可以自行网路查找解说,解说百百种。 傻眼貓咪 发表于 2021-10-31 10:44
基本上大部分程序语言都是单一线程的,意思就是先做完一个事情,然后才做另外一个事情,这是肯定的。
Pyth ...
额还是没有回答我上面的实际问题啊,同样await,为啥第二种是按顺序执行的。 从入门到富豪 发表于 2021-10-31 10:56
额还是没有回答我上面的实际问题啊,同样await,为啥第二种是按顺序执行的。
这是优先执行问题
第一个封装会先检测外面的 await (外面两个 await 都有联系)然后再检测 ceshi01() 函数里的 await,所以 group_a 和 group_b 是同时检测的。
第二个封装外面的三个 await 并没有联系,所以真正是一个个执行,由上往下
本帖最后由 傻眼貓咪 于 2021-10-31 11:23 编辑
从入门到富豪 发表于 2021-10-31 10:56
额还是没有回答我上面的实际问题啊,同样await,为啥第二种是按顺序执行的。
为了避免这种问题,最好创建事件循环 asyncio.get_event_loop() 和 待处理事件列表,想执行时,则用 run_until_complete() 执行便可。如:import asyncio
import time
async def A():
await asyncio.sleep(3)
print("我是洗衣机 A")
async def B():
await asyncio.sleep(1)
print("我是洗衣机 B")
async def C():
await asyncio.sleep(2)
print("我是洗衣机 C")
loop = asyncio.get_event_loop() # 事件循环
task = # 待处理事件
start = time.time()
loop.run_until_complete(asyncio.wait(task)) # 执行所有事件
end = time.time()
loop.close()
print(f"一共耗时 {int(end - start)} 秒")我是洗衣机 B
我是洗衣机 C
我是洗衣机 A
一共耗时 3 秒 无论是create_task还是asyncio.gather都会把协程对象及其各种状态打包注册为 Task 对象,
你可以把它的作用当成是放入任务队列中,并返回
task1 = asyncio.create_task(coro1)
task2 = asyncio.create_task(coro1)
await task1
await task2
无疑是往队列塞进去2个 再清空队列
awaitasyncio.create_task(coro1)
awaitasyncio.create_task(coro1)
则是 塞进去了就清空队列
asyncio.gather的返回值是保住了参数顺序的无关乎执行顺序 {:10_338:}建议去学一下JavaScript的asyncfunction和Promise对象 你这个自然就通了 它和py的Future对象差不多
我都没怎么看python的asyncio都会了然后去学个fastapi岂不美哉
js只有asyncfunction和其返回的Promise对象两层 毕竟事件循环是自带的缺点是没有sleep函数能卡住进程
而py有asyncfunction 返回coro 然后coro打包成task task是Future的子类 3层结构 事件循环还得手动开启
最重要的思想其实就一个 生产者消费者模型 而已 kogawananari 发表于 2021-10-31 12:17
无论是create_task还是asyncio.gather都会把协程对象及其各种状态打包注册为 Task 对象,
你可以把它的作 ...
清空队列是啥意思,1和2的写法 感觉是对等的啊,
关于gather保持顺序的事情
我专门搞个栗子,gather的列表不是按顺序执行的,假设是按顺序执行的, 结果的返回也不是按顺序返回的, 我这个是因为有个迭代器,才保证取值顺序。
mylist = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
class AA():
def __init__(self):
self.g = self.getone() #生成了迭代器,放在init里面
def getone(self):
for i in mylist:
yield i#返回子集列表,返回的顺序是a—z
async def xieru(self,id,delay):
while True:
await asyncio.sleep(delay)
# time.sleep(delay)
print(id,next(self.g))
async def main(self):
task1 =asyncio.create_task(self.xieru("AA",4)) #线程AA 执行的内部顺序abc,def,等是固定的
task2 =asyncio.create_task(self.xieru("BB",2))#线程BB
task3 = asyncio.create_task(self.xieru("CC",3))#线程CC
tasks =
await asyncio.gather(*tasks)#gather返回的列表,并不能按带入的顺序排列,而是按实际执行完成的顺序排列。
if __name__ =='__main__':
aa = AA()
asyncio.run(aa.main()) #运行的程序要放在最外面 傻眼貓咪 发表于 2021-10-31 11:17
为了避免这种问题,最好创建事件循环 asyncio.get_event_loop() 和 待处理事件列表,想执行时,则用 ru ...
目前python采用了高级的 gather来设置,不用手动用loop了,我感觉还是对await的 对象的 理解不够深刻。 从入门到富豪 发表于 2021-10-31 13:15
清空队列是啥意思,1和2的写法 感觉是对等的啊,
关于gather保持顺序的事情
import asyncio
async def count(m):
await asyncio.sleep(3)
print(m)
return m
async def main():
aws =
print(await asyncio.gather(*aws))
asyncio.run(main())
按顺序返回 从入门到富豪 发表于 2021-10-31 13:17
目前python采用了高级的 gather来设置,不用手动用loop了,我感觉还是对await的 对象的 理解不够深刻。
抱歉,帮不到你了,兄弟 从入门到富豪 发表于 2021-10-31 13:15
清空队列是啥意思,1和2的写法 感觉是对等的啊,
关于gather保持顺序的事情
import asyncio
async def count1():
await asyncio.sleep(1)
print("\n\n----------任务队列里目前有----------")
for task in asyncio.all_tasks():
print(task.get_coro())
print('----------count1任务运行完毕----------\n\n')
async def count2():
await asyncio.sleep(2)
print("\n\n----------任务队列里目前有----------")
for task in asyncio.all_tasks():
print(task.get_coro())
print('----------count2任务运行完毕----------\n\n')
async def main():
'''await asyncio.create_task(count1())
await asyncio.create_task(count2())'''
task1 = asyncio.create_task(count1())
task2 = asyncio.create_task(count2())
await task1
await task2
print('\n----------main任务运行完毕----------\n')
asyncio.run(main())
我帮你把任务改成了输出当前所有任务 你再把注释放出来换成直接await 对比一下 kogawananari 发表于 2021-10-31 13:34
import asyncio
async def count1():
亲 除了发现这一种比第一种执行更快一些,还是没看出啥啊, 或者说看不懂 。第一种执行时间明显长很多。 从入门到富豪 发表于 2021-10-31 22:59
亲 除了发现这一种比第一种执行更快一些,还是没看出啥啊, 或者说看不懂 。第一种执行时间明显长很多。
一个是继发执行 一个是并发执行
输出的任务队列一看就知道区别了 从入门到富豪 发表于 2021-10-31 22:59
亲 除了发现这一种比第一种执行更快一些,还是没看出啥啊, 或者说看不懂 。第一种执行时间明显长很多。
继发运行的时候 队列塞进去一个任务马上就把那个任务做了 明显每次只打印了自己和main
并发运行的时候 队列塞进去两个一起执行的执行的时候打印出了自己和main 之间还有其他任务 kogawananari 发表于 2021-10-31 23:32
继发运行的时候 队列塞进去一个任务马上就把那个任务做了 明显每次只打印了自己和main
并发运行的时候 ...
这个结果我知道呀, 问题是为啥会这样呢, 这个写法执行逻辑或者顺序是如何操作的呢, 毕竟从写法来讲, 2个写法几乎是对等的呀。
页:
[1]