我目前正在使用asyncio.wait
我需要一个特殊的函数来在所有其他await
上运行
import asyncio
async def special_function():
while True:
# does some work,
# Passes control back to controller to run main_tasks
# if they are no longer waiting.
await asyncio.sleep(0)
async def handler():
tasks = [task() for task in main_tasks]
# Adding the task that I want to run when all main_tasks are awaiting:
tasks.append(special_function())
await asyncio.wait(tasks)
asyncio.get_event_loop().run_until_complete(handler())
如何让special_function
仅在所有main_tasks
都在await
上时运行?
我的意思是“所有main_tasks
都在await
上:所有main_tasks
还没有准备好继续,例如处于asyncio.sleep(100)
或I / O绑定中,并且仍在等待数据。
因此,main_tasks
无法继续执行,并且任务处于这种状态时,事件循环运行special_function
,而不是事件循环的每次迭代。
我的用例:
main_tasks
正在使用来自Web套接字的新数据更新数据结构。
special_function
根据来自该进程的更新信号将该数据传输到另一个进程。 (multiprocessing
具有共享变量和数据结构)
它必须是传输时最新的数据,不能有来自main_tasks的待处理更新。
这就是为什么我只在没有main_tasks包含新数据可处理的情况下才想运行special_function的原因。 (即所有在await
上等待)
答案 0 :(得分:3)
我试图为“任务未准备好运行”条件编写测试。我认为asyncio不会公开调度程序的详细信息。开发人员明确表示,他们希望保留更改异步内部结构的自由,而又不会破坏向后兼容性。
在asyncio.Task
中有此注释(注意:_step()
运行任务协程直到下一次等待):
# An important invariant maintained while a Task not done:
#
# - Either _fut_waiter is None, and _step() is scheduled;
# - or _fut_waiter is some Future, and _step() is *not* scheduled.
但是,该内部变量当然不在API中。
通过读取_fut_waiter
的输出,您可以对repr(task)
进行有限的访问,但是格式似乎也不可靠,因此我不会依赖这种方式:
PENDINGMSG = 'wait_for=<Future pending '
if all(PENDINGMSG in repr(t) for t in monitored_tasks):
do_something()
无论如何,我认为您正在尝试变得过于完美。您想知道其他任务中是否有新数据。如果数据在异步缓冲区中怎么办?内核缓冲区?网卡接收缓冲区? ...您永远不知道下一毫秒是否会收到新数据。
我的建议:将所有更新写入单个队列。检查该队列作为唯一的更新源。如果队列为空,请发布最后一个状态。
答案 1 :(得分:2)
这就是我要做的:
我不会使用您的特殊功能。
每次数据更新都需要一个单独的世代ID(4字节整数),我只将ID放在共享内存中。
我认为两个进程都是独立运行的。
订户将世代ID保留为本地。当它发现共享内存中的世代ID已更改时,则将从文件中读取新数据。
数据存储在tmpfs(/ tmp)上,因此它在内存中。如果合适,您可以创建自己的tmpfs。很快。
这是原因:
因此,当您的一项任务收到新数据时,打开一个文件,对其进行写入,然后在关闭文件描述符后,将世代ID写入共享内存。在更新世代ID之前,您可以安全地删除文件。订户-如果已打开文件,它将完成文件的读取,如果尝试打开它,则它将无法打开,因此无论如何都必须等待下一代。 如果计算机崩溃,则/ tmp消失了,因此您不必担心清理文件。您甚至可以编写一个新任务,即如果需要的话,可以删除/ tmp中较早的文件。
答案 2 :(得分:1)
当事件循环运行某些任务时,此任务将一直执行,直到将控制权返回给事件循环为止。通常,只有一个原因是任务希望将控制权返回到事件循环:任务如果遇到阻塞操作(因此“准备不继续”)。
这意味着“事件循环的每次迭代”通常等于“所有main_tasks
都在await
上”。您已经拥有的代码将(通常)根据需要工作。您唯一要做的就是to make special_function()
任务。
在遇到“真正的”阻塞调用之前,任务很有可能将控制权返回事件循环,通常看起来像await asyncio.sleep(0)
(就像您在special_function
中所做的那样)。这意味着任务要确保所有其他任务在继续执行之前被调用:您可能希望尊重这一点。
答案 3 :(得分:0)
为什么不使用semaphore
async def do_stuff(semaphore):
async with semaphore:
await getting_stuff_done()
async def wait_til_everyone_is_busy(semaphore):
while not semaphore.locked():
await asyncio.sleep(1)
do_other_stuff()
为了更好地说明我的观点,请举一个简单的例子:
import asyncio
import time
async def process(semaphore, i):
while True:
print(f"{i} I'm gonna await")
await asyncio.sleep(1)
async with semaphore:
print(f'{i} sleeping')
await asyncio.sleep(3)
print(f'{i} done sleeping')
print(f"{i} I'm gonna await again")
await asyncio.sleep(1)
async def other_process(semaphore):
while True:
while not semaphore.locked():
print("Everyone is awaiting... but I'm not startingr")
await asyncio.sleep(1)
print("Everyone is busy, let's do this!")
time.sleep(5)
print('5 seconds are up, let everyone else play again')
await asyncio.sleep(1)
semaphore = asyncio.Semaphore(10)
dataset = [i for i in range(10)]
loop = asyncio.new_event_loop()
tasks = [loop.create_task(process(semaphore, i)) for i in dataset]
tasks.append(loop.create_task(other_process(semaphore)))
loop.run_until_complete(asyncio.wait(tasks))
我们创建10个使用“进程”功能的任务,以及一个使用“ other_process”的任务。一个执行“ other_process”的程序只能在所有其他程序都保持该信号量的同时运行,并且由于Asyncio上下文切换的工作方式,只有在其他程序处于等待状态时才执行“ other_process”功能,直到“ other_process”命中自己的“ await”。
$ python3 tmp
0 I'm gonna await
1 I'm gonna await
2 I'm gonna await
3 I'm gonna await
4 I'm gonna await
5 I'm gonna await
6 I'm gonna await
7 I'm gonna await
8 I'm gonna await
9 I'm gonna await
Everyone is awaiting... but I'm not startingr
0 sleeping
1 sleeping
2 sleeping
3 sleeping
4 sleeping
5 sleeping
6 sleeping
7 sleeping
8 sleeping
9 sleeping
Everyone is busy, let's do this!
5 seconds are up, let everyone else play again
0 done sleeping
0 I'm gonna await again
1 done sleeping
1 I'm gonna await again
2 done sleeping
2 I'm gonna await again
3 done sleeping
3 I'm gonna await again
4 done sleeping
4 I'm gonna await again
5 done sleeping
5 I'm gonna await again
6 done sleeping
6 I'm gonna await again
7 done sleeping
7 I'm gonna await again
8 done sleeping
8 I'm gonna await again
9 done sleeping
9 I'm gonna await again
Everyone is awaiting... but I'm not startingr
0 I'm gonna await
1 I'm gonna await
2 I'm gonna await
3 I'm gonna await
4 I'm gonna await
5 I'm gonna await
6 I'm gonna await
7 I'm gonna await
8 I'm gonna await
9 I'm gonna await
Everyone is awaiting... but I'm not startingr
0 sleeping
1 sleeping
2 sleeping
3 sleeping
4 sleeping
5 sleeping
6 sleeping
7 sleeping
8 sleeping
9 sleeping
Everyone is busy, let's do this!
答案 4 :(得分:0)
将输入和输出请求都推送到PriorityQueue上,输入优先于输出。然后只需正常处理队列中的任务,它将始终在所有输出请求之前完成所有未完成的输入请求。
所以您的主循环将包含以下内容:
这可能意味着您必须将所有现有任务的逻辑拆分为单独的套接字侦听器和实际任务处理,但这不一定是一件坏事。