pythonmultithreadingflaskasynchronouspython-asyncio

RuntimeError: You cannot use AsyncToSync in the same thread as an async event loop


I'm making a dashboard in python, using flask. It needs to run three tasks: flask, datastream_handler(), and dashboard_handler(). One of these tasks, datastream_handler(), is required to be an async function.

I've used threading to allow the tasks to run at the same time, (flask and dashboard_handler() are threads, datastream_handler() is run from the main thread).

dashboard_handler() and datastream_handler() start up and run perfectly, and flask appears to start but when I visit the webpage it gives me an error in terminal: RuntimeError: You cannot use AsyncToSync in the same thread as an async event loop - just await the async function directly.

I'm confused, because I have awaited the async function (datastream_handler) directly?

I've tried switching it around so that flask is the main thread, and datastream_handler() is started as a threading.Thread (using asyncio.run to start the async function from a non-async function). However, this just gave the same error.

I looked up this error and a couple of people had the same error with Django and requests-html, but their findings were specific to their respective framework and couldn't be applied to flask.

Here is my code:

datastream_handler and dashboard_handler are in different files; dashboard_handler is just a normal synchronous function with a while loop and datastream_handler is an async function which currently only contains a while loop with print and asyncio.sleep, for testing purposes.

I don't think the contents of these functions are causing the errors, but correct me if I am wrong

app = Flask(__name__)
socket = SocketIO(app)

@app.route("/")
async def index():
    return render_template("index.html")

def start_flask():
    socket.run(app, host="0.0.0.0", port=5000)
        
async def main():

    q = Queue() #queue for data transfer
    threading.Thread(target=dashboard_handler, args=(q,), daemon=True).start()
    threading.Thread(target=start_flask, daemon=True).start()

    await datastream_handler(q)


if __name__ == "__main__":
    asyncio.run(main())

If anyone can help I would be very grateful

Thanks


Solution

  • What is the reason putting Flask in thread? Flask app should be initiated in the main thread.

    About this error, RuntimeError: You cannot use AsyncToSync in the same thread as an async event loop - just await the async function directly.

    I think this come from 2 reasons:

    1. You're running the Flask in thread
    2. You're using async def index() which flask will turn it into threaded async using asgiref's module AsyncToSync and at it can't be done in thread.

    If you want to run 3 of those concurrently using thread and one of it is asynchronous maybe you can use python-worker link

    1. Define datastream_handler and dashboard_handler as a worker
    from worker import worker, async_worker
    
    @async_worker
    async def datastream_handler(q):
        ...
    
    @worker
    def dashboard_handler(q):
        ...
    
    1. In your main file :
    if __name__ == "__main__":
        q = Queue()
        asyncio.run(datastream_handler(q))
        dashboard_handler(q)
        socket.run(app, host="0.0.0.0", port=5000)
    

    datastream_handler and dashboard_handler will automatically run as a thread.