Python asyncio and threading

Asked

Viewed 782 times

0

I’m studying the module asyncio of Python and there is the function run_coroutine_threadsafe which should be executed in a different thread than the event loop. Follow my script:

#!usr/bin/python3
# -*- coding: utf-8 -*-

import asyncio
import threading


def target():
    print('Iniciou a thread')
    #asyncio.set_event_loop(None)
    #loop = asyncio.new_event_loop()
    asyncio.run_coroutine_threadsafe(blah(), loop)
    print('Finalizou a thread')


async def blah():
    print('Thread atual:', threading.current_thread().name)


async def main():
    thread = threading.Thread(target=target, args=(), kwargs={}, name='Minha Thread')
    thread.setDaemon(True)
    thread.start()
    thread.join()
    print('finalizou')


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(main())
    finally:
        loop.close()

# Output
Iniciou a thread
Finalizou a thread
finalizando

The problem is that the corotina blah is never invoked by the function run_coroutine_threadsafe but I can’t find the reason, I even tried to instantiate a new loop of events (as you can see in the commented lines) but even so the script doesn’t work. What am I missing?

  • There was no exit in the finished.

  • I switched out the print for sys.stdout.write but it didn’t even work out that way.

  • I must have been wrong about that. I’m sorry.

  • That I appreciate you trying to help me.

  • From what I understand, reading over the documentation, you don’t need to create a thread for this, the function itself run_coroutine_threadsafe will run into another thread.

  • I thought so, too, but when I make the call run_coroutine_threadsafe of MainThread the printed result is "MainThread", he creates no other thread, searching found in the documentation a function run_in_executor that makes the creation of another thread.

Show 1 more comment

1 answer

1


The problem is you’re calling loop.close() before he has the opportunity to run the routine blah.

The routine blah is scheduled to run in the loop, but the main thread (which has the loop) is stuck in main()... She never gave control to the loop so that it runs other routines, and when main() ends the loop is closed.

For example, placing 1 second hold before the loop ends:

...
thread.join()
await asyncio.sleep(1)
print('finalizou')
...

Put this on await sleep in main() between the join() and the print as in the example and will get the result you expect. Because the await returns control for loop.

Another example is to use an event to signal the end of the program:

import asyncio
import threading

def target(e):
    print('Iniciou a thread')
    asyncio.run_coroutine_threadsafe(blah(e), loop)
    print('Finalizou a thread')

async def blah(e):
    print('Thread atual:', threading.current_thread().name)
    e.set() # sinaliza o final da rotina, autorizando o loop a terminar


async def main():
    e = asyncio.Event()
    thread = threading.Thread(target=target, args=(e,), kwargs={}, name='Minha Thread')
    thread.setDaemon(True)
    thread.start()
    await e.wait() # devolve o controle ao loop e espera que
                   # a rotina sinalize o fim do programa
    print('finalizou')

That way before finishing the loop, we wait for the execution of the routine blah using an event asyncio.Event().

  • Waiting for the completion of the thread with the method join() occurs only if control is returned to event loop?

  • It means that even if I book a corotina in a thread different if the event loop is the same need to give control to the event loop for it to be executed?

  • @Thiagoluizs First question: no, you can withdraw the join() that will work the same way. What you can’t do is finish the loop... Your loop has to stay active, always expecting something - in the example was the sleep but in a real program the loop probably stays most of the time waiting for io.

  • @Thiagoluizs second question: Do you use run_coroutine_threadsafe to schedule an corotina that will run on the main thread when the loop has time to do so. The error of your code was only to have finished the loop - usually the loop is waiting for something to happen, until it finishes everything that the program has to do. When there’s nothing left to look forward to, then you call loop.close().

  • If run_coroutine_threadsafe is executed in MainThread the reason for it to be threadsafe?

  • @Thiagoluizs asynchronous routines only run on the main thread, where the loop is - if you have other threads that need to start asynchronous routines, you need to use this to schedule something running on the main thread.

  • I have to call this method the MainThread and how he will be executed in another thread ?

  • @Thiagoluizs run_coroutine_threadsafe can be run from any thread. The corotina you pass will rotate in the loop, ie will rotate in the main thread.

  • So run_coroutine_threadsafe is invoked in another thread causing a corotina spin in the event loop of MainThread ?

  • @Thiagoluizs yes, as asynchronous objects and chorotine objects are not threadsafe, you need to use run_coroutine_threadsafe every time you need to run something in the loop, because you’re on another thread and the corollaries need to run on the main thread where the loop is. That’s what this function is for run_coroutine_threadsafe - to allow you to run asynchronous tasks from a thread that is not the same where the loop is.

  • Got it. Thank you so much for everything.

Show 6 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.