RogB
RogB

Reputation: 461

Asyncio to get the status of a long-running code

I have some code that will be running in a server. It can take a long time to run (minutes at least). I want to be able to poll the server for where in the code it currently is. I thought I could use asyncio to do that, but it looks like it may not be what I need.

Here is some code that I wrote to test it (saved as test_async.py):

import asyncio
import time

the_status = 'idle'


async def waiting():
    global the_status
    await asyncio.sleep(0.001)
    the_status = 'running'
    time.sleep(30)
    the_status = 'finished'


def get_status():
    global the_status
    return the_status


async def main():
    loop.create_task(waiting())


loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

I run this by opening the Python console and typing:

from test_async import *

What I expected to happen is that it would start running waiting(), change the_status to 'running', and then wait 30 seconds before turning the status to 'finished'. In the meantime, I should get the console prompt back and I would be able to get the current status by typing get_status().

What is actually happening is that the variable the_status is never changing from its initial state of 'idle'.

Am I doing something wrong? Is asyncio not the answer to what I'm trying to do?

My Python version is 3.6.

Upvotes: 0

Views: 1968

Answers (1)

user4815162342
user4815162342

Reputation: 155600

Am I doing something wrong?

The code has two issues:

  • main just creates a task without awaiting it - you can think of create_task as creating a "background" task. But in asyncio background tasks run only as long as the main loop does, so run_until_complete(main()) exits immediately because main() returns immediately after creating the task. With the main loop stopping, the waiting task doesn't have a chance to start executing.

  • waiting calls time.sleep, which is not allowed in asyncio. Asyncio is a cooperative multi-tasking system for JS-style callbacks and coroutines which suspend themselves when they await something that blocks. time.sleep doesn't suspend, it just blocks the whole event loop thread. Executing legacy blocking code inside asyncio is correctly done with run_in_executor.

Is asyncio not the answer to what I'm trying to do?

If you have some blocking code that you need executed "in the background", you should use threads.

import time, concurrent.futures

the_status = 'idle'

def waiting():
    global the_status
    time.sleep(0.001)
    the_status = 'running'
    time.sleep(30)
    the_status = 'finished'

executor = concurrent.futures.ThreadPoolExecutor()
executor.submit(waiting)

Importing the code works as expected:

>>> import thr2
>>> thr2.the_status
'running'

Upvotes: 1

Related Questions