【发布时间】:2021-11-29 02:14:21
【问题描述】:
下面的示例纯粹是理论上的,用于传达我在这里想要实现的目标。
我有几个名字——杰克、爱丽丝、鲍勃
其中一个人有姓——墨菲
我可以进行网络调用来检索全名。一旦我找到姓“墨菲”的人,我就很高兴。
async def get_persons():
persons = await asyncio.gather(
get_person("Jack"),
get_person("Alice"),
get_person("Bob"))
for person in persons:
if person.surname == "Murphy":
return person
def main():
person = asyncio.run(get_persons())
print(f"{person.first_name} has surname {person.last_name}")
当然这里的问题是我们必须等待所有 3 个请求完成。
所以最短等待时间是所有 3 个请求的最长请求时间。
有 3 个网络请求。
假设第一个需要 3 秒,第二个需要 1 秒,第三个需要 6 秒。
运行此代码需要 6 秒。
但是我们看到第二个请求(Alice)的姓氏是 Murphy,并且显然在 1 秒后完成。
我们可以基本上忽略其他两个网络请求,并在此时返回吗?
所以最终,整个过程需要 1 秒,而不是 6 秒。
编辑:
(代码更新以反映 Ajax1234 的解决方案)
class Persons:
def __init__(self):
self.p = []
def get_person_request(self, name):
if name == "Alice":
print("Searching Alice")
time.sleep(6)
print("Returning Alice")
return {'firstname': "Alice", 'surname': "Donnelly"}
if name == "Bob":
print("Searching Bob")
time.sleep(3)
print("Returning Bob")
return {'firstname': "Bob", 'surname': "Murphy"}
if name == "Jack":
print("Searching Jack")
time.sleep(8)
print("Returning Jack")
return {'firstname': "Jack", 'surname': "Connell"}
return None
async def get_person(self, n, _id):
# the process for checking if the request response returns a person with the target surname
if (person := self.get_person_request(n))["surname"] == "Murphy":
for i, a in self.p:
if i != _id:
a.cancel()
return person
async def get_persons(self, names):
print("Setting tasks...")
self.p = [(i, asyncio.create_task(self.get_person(a, i)))
for i, a in enumerate(names)]
print("Gathering async results...")
persons = await asyncio.gather(*[a for _, a in self.p])
return [person for person in persons if isinstance(person, dict)][0]
def test():
val = asyncio.run(Persons().get_persons(['Bob', 'Alice', 'Jack']))
print(val)
脚本的输出看起来像
Setting tasks...
Gathering async results...
Searching Bob
Returning Bob
asyncio.exceptions.CancelledError
我希望输出看起来像
Setting tasks...
Gathering async results...
Searching Bob
Searching Alice
Searching Jack
Returning Bob
{'firstname': 'Bob', 'surname': 'Murphy'}
这里有两个问题:
- 为什么每个 get_person 任务没有异步运行?
- 如何处理
gather()不允许取消任务的异常?
【问题讨论】:
-
如果您只是等待
gather(),他们甚至会在您参加测试之前返回。但是,您可以将get_person包装在一个函数中,该函数在找到肯定结果时设置一个标志,并在您的 main 循环中检查它,在设置标志时取消剩余的任务。 -
我们如何取消剩余的任务?
-
asyncio.as_completed能满足您的需求吗?
标签: python async-await python-asyncio