简体   繁体   中英

How to run async function multiple times?

How can I run function multiple times?

I'd like to re-run loop so that program executes function a few more times. I understand that this is not correct approach. But surely there is workaround?

My code:

from asyncio import get_event_loop, gather, sleep


async def main(k):
    print(k)
    await sleep(1)


if __name__ == '__main__':
        count_run = 3
        count_group = 3
        list_objects = list()

        for i in range(1, 11):
        list_objects.append(i)

        list_func = [main(x) for x in list_objects]

        run_groups = [list_func[i:i + count_group] for i in range(0, len(list_func), count_group)]

        loop = get_event_loop()

        for _ in range(count_run):
           for rg in run_groups:
               loop.run_until_complete(gather(*rg))

Now I am getting an error:

RuntimeError: cannot reuse already awaited coroutine

You create a list of awaitables by calling main() in list_func for each element of list_objects and then you create a bunch of slices of that list in run_groups , dividing the awaitable in groups of 3. You then start those groups one at a time, causing the awaitables to execute and return - once you have started every group and every awaitable has been awaited, you cannot await them again, you will have to create new awaitables.

The easiest way to fix your code to work, is to just change the order and include the creation of awaitables in the outer loop:

from asyncio import get_event_loop, gather, sleep


async def main(k):
    print(k)
    await sleep(1)


if __name__ == '__main__':
    count_run = 3
    count_group = 3
    list_objects = list()

    for i in range(1, 11):
        list_objects.append(i)

    loop = get_event_loop()

    for _ in range(count_run):
        list_func = [main(x) for x in list_objects]

        run_groups = [list_func[i:i + count_group] for i in range(0, len(list_func), count_group)]
        for rg in run_groups:
           loop.run_until_complete(gather(*rg))

Note: I also fixed the problems with indentation in your code example, assuming that those were just a result of copy/paste issues into the question text.

The code now works because every awaitable in list_func only gets awaited once (in groups of 3) by the gather call, and all of them get awaited before the loop completes. In every iteration of the outer loop, a new list of awaitables gets created, to be able to repeat the process, since every awaitables can only be awaited once.

Another way to do it would be to create all the awaitables first and then cycle through all of them:

from asyncio import get_event_loop, gather, sleep


async def main(k):
    print(k)
    await sleep(1)


if __name__ == '__main__':
    count_run = 3
    count_group = 3
    list_objects = list()

    for i in range(1, 11):
        list_objects.append(i)

    loop = get_event_loop()

    list_func = []
    for _ in range(count_run):
        list_func.extend([main(x) for x in list_objects])

    run_groups = [list_func[i:i + count_group] for i in range(0, len(list_func), count_group)]
    for rg in run_groups:
       loop.run_until_complete(gather(*rg))

From the difference in timing, you can tell that it now keeps executing in groups of 3, but continues "7 8 9", "10 1 2", etc. until it has gone through all 30.

The key difference is that list_func now contains all 30 awaitables before any of them get executed, instead of new awaitables getting created 3 times.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM