Asynchronous 在不等待其他协同路由完成的情况下重新启动异步协同路由
我正在用Python进行异步编程,遇到以下问题: 模拟多人从同一个食物碗中吃一定数量的食物。每个人一次可以吃x份食物,然后咀嚼食物y秒(模拟阻塞呼叫)。只要碗里还有食物,一个人就可以独立于他人进食和咀嚼食物 为每个食客和食物碗定义类。最终目标是在FoodBowl类中提供一个函数,该函数接受一个人员列表,并让他们开始从碗中进食,直到碗空为止。每当有人从碗里拿食物时,都应该在标准输出上打印一条信息 例如,如果我有一个盛有25份食物的碗,还有三个人,a、B和C:Asynchronous 在不等待其他协同路由完成的情况下重新启动异步协同路由,asynchronous,async-await,python-asyncio,python-3.7,Asynchronous,Async Await,Python Asyncio,Python 3.7,我正在用Python进行异步编程,遇到以下问题: 模拟多人从同一个食物碗中吃一定数量的食物。每个人一次可以吃x份食物,然后咀嚼食物y秒(模拟阻塞呼叫)。只要碗里还有食物,一个人就可以独立于他人进食和咀嚼食物 为每个食客和食物碗定义类。最终目标是在FoodBowl类中提供一个函数,该函数接受一个人员列表,并让他们开始从碗中进食,直到碗空为止。每当有人从碗里拿食物时,都应该在标准输出上打印一条信息 例如,如果我有一个盛有25份食物的碗,还有三个人,a、B和C: A每次吃2份食物,咀嚼3秒钟 B一次
- A每次吃2份食物,咀嚼3秒钟
- B一次吃3份食物,咀嚼4秒钟
- C每次吃5份食物,咀嚼2秒钟
(t=0) Person A takes 2 servings of food, leaving 23 servings in the bowl.
(t=0) Person B takes 3 servings of food, leaving 20 servings in the bowl.
(t=0) Person C takes 5 servings of food, leaving 15 servings in the bowl.
(t=2) Person C takes 5 servings of food, leaving 10 servings in the bowl.
(t=3) Person A takes 2 servings of food, leaving 8 servings in the bowl.
(t=4) Person B takes 3 servings of food, leaving 5 servings in the bowl.
(t=4) Person C takes 5 servings of food, leaving 0 servings in the bowl.
(t=4) The bowl is empty!
(在两个人准备再吃一份的时候,如t=4
,顺序无关紧要)
代码是我的尝试:
导入异步IO
导入时间
类Person():
定义初始(自我、姓名、服务大小、进食时间):
self.name=名称
自助式大小=自助式大小
self.time\u to\u eat=吃饭时间
异步def eat_from(self,foodbowl):
食用量=自助式大小(如果foodbowl.qty>=自助式大小,否则foodbowl.qty>=自助式大小)
foodbowl.qty-=食用量
t=round(time.time()-foodbowl.start\u时间)
打印(“(t={})人{}拿起{}份食物,把{}份食物留在碗里。”.format(t,self.name,吃的份数,foodbowl.qty))
等待异步睡眠(自我进食时间)
退换货
类FoodBowl():
定义初始(自身,数量):
自身数量=数量
异步定义分配_eaters(self,eaters):
self.start\u time=time.time()
当self.qty>0时:
等待asyncio.gather(*[eater.eat\u from(self)for eater in eater])
t=整周(time.time()-self.start\u时间)
打印(“碗是空的!”)
碗=FoodBowl(25)
person_1=person(“A”,2,3)
person_2=person(“B”,3,4)
person_3=person(“C”,5,2)
asyncio.run(bowl.assign_-eaters([person_1,person_2,person_3]))
但是,我的尝试会导致以下行为:
(t=0) Person A picks up 2 servings of food, leaving 23 servings in the bowl.
(t=0) Person B picks up 3 servings of food, leaving 20 servings in the bowl.
(t=0) Person C picks up 5 servings of food, leaving 15 servings in the bowl.
(t=4) Person A picks up 2 servings of food, leaving 13 servings in the bowl.
(t=4) Person B picks up 3 servings of food, leaving 10 servings in the bowl.
(t=4) Person C picks up 5 servings of food, leaving 5 servings in the bowl.
(t=8) Person A picks up 2 servings of food, leaving 3 servings in the bowl.
(t=8) Person B picks up 3 servings of food, leaving 0 servings in the bowl.
(t=8) Person C picks up 0 servings of food, leaving 0 servings in the bowl.
The bowl is empty!
可以看出,每个人都会等待每个人吃完自己的菜,然后再伸手去拿碗。看看我的代码,我知道这是因为我在eat函数上等待了asyncio.gather()
,因此它将等待所有三个人吃完,然后再开始吃饭
我知道这是错误的,但我不知道我可以在asyncio
库中使用什么来解决这个问题。我想到的是,只要碗里还有食物,coroutine就会自动重启。我如何做到这一点,或者有更好的方法解决这个问题
我知道[等待所有三个人吃完后再开始吃饭]是错误的,但我不知道我可以在asyncio库中使用什么来解决这个问题
您可以使用wait(return\u when=asyncio.FIRST\u COMPLETED)
来等待任何食客完成,而不是像当前代码那样等待所有食客完成。每当一个食客完成进食时,为同一个食客生成一个新的协同程序,有效地“重新启动”它。这需要wait
返回给eater的任务的引用;这样的引用可以很容易地附加到任务
对象。代码可能如下所示:
async def assign_eaters(self, eaters):
self.start_time = time.time()
# create the initial tasks...
pending = [asyncio.create_task(eater.eat_from(self))
for eater in eaters]
# ...and store references to their respective eaters
for t, eater in zip(pending, eaters):
t.eater = eater
while True:
done, pending = await asyncio.wait(
pending, return_when=asyncio.FIRST_COMPLETED)
if self.qty == 0:
break
for t in done:
# re-create the coroutines that have finished
new = asyncio.create_task(t.eater.eat_from(self))
new.eater = t.eater
pending.add(new)
t = round(time.time() - self.start_time)
print("The bowl is empty!")
这导致了预期的输出,但代价是一些复杂性。但是,如果你准备改变你的方法,有一个简单得多的可能性:让每个吃的人成为一个独立的演员,继续吃,直到碗里没有更多的食物。那么你就不需要“重启”食客,因为他们一开始就不会离开,至少只要碗里有食物:
async def eat_from(self, foodbowl):
while foodbowl.qty:
servings_taken = self.serving_size \
if foodbowl.qty >= self.serving_size else foodbowl.qty
foodbowl.qty -= servings_taken
t = round(time.time() - foodbowl.start_time)
print("(t={}) Person {} picks up {} servings of food, "
"leaving {} servings in the bowl."
.format(t, self.name, servings_taken, foodbowl.qty))
await asyncio.sleep(self.time_to_eat)
assign\u eaters
不再需要循环,而是使用简单的聚集:
async def assign_eaters(self, eaters):
self.start_time = time.time()
await asyncio.gather(*[eater.eat_from(self) for eater in eaters])
t = round(time.time() - self.start_time)
print("The bowl is empty!")
这段更简单的代码再次产生了预期的输出。唯一的“缺点”是改变需要反转控制:碗不再驱动进食过程,现在由每个进食者自主完成,碗被动地等待他们完成。然而,从问题陈述来看,这似乎不仅是可以接受的,甚至可能是追求的解决方案。据说,食物碗功能应该让人们“开始从碗里吃东西,直到碗空为止”。“开始吃”意味着碗只是启动了这个过程,每个人都自己吃——这就是第二个版本的工作原理