There may be one other case the place our program already implements a loop within the current code. For instance, most GUI applications use an occasion loop to answer numerous occasions and to replace the UI.
Let’s take tkinter for instance. tkinter will begin a predominant loop when it begins, and this predominant loop will block the primary thread and carry on looping. As proven within the determine beneath:
A direct name to synchronous IO code will block the primary loop
Let’s take the instance of a tkinter program that accommodates a button and a standing textual content:
This program makes use of a state machine to implement it. Each 60 milliseconds, the code refreshes the corresponding textual content based on this system’s present state.
After we click on the request_code button, the workflow ought to ideally appear to be the next diagram:
However by the execution consequence, this system hangs when clicking the button, and the standing textual content is up to date till the IO blocking code finishes executing. It signifies that the primary loop is blocked when the IO request is operating, inflicting the GUI interface to be unresponsive:
Utilizing asyncio.run to Run Asyncio Code
Can we substitute the requests package deal with the aiohttp package deal to realize the asynchronous invocation of IO requests?
Right here we first inherit the App
class to implement a brand new class AppAsyncBase
. On this new class, we use aiohttp to implement an async_request
technique to put the muse for subsequent asynchronous calls:
Readers of my earlier article will know we will execute asynchronous strategies inside synchronous code by way of asyncio.run
:
Then, we implement a brand new class AppAsyncRun
, by inheriting AppAsyncBase
. On this new class, we override the request_remote
technique and use asyncio.run
to name the async_request
technique instantly:
Subsequent, let’s have a look at the outcomes. As a result of asyncio’s occasion loop is executed in the primary thread by default, and when the occasion loop is operating, it blocks the primary thread, and the primary loop of tkinter is blocked and unresponsive:
Integrating Asyncio with Thread-Based mostly Applications
Is there a strategy to resolve the occasion loop blocking downside?
Right here we will use a separate daemon thread after which run the occasion loop into the daemon thread, so asyncio’s occasion loop won’t block the primary thread. The diagram is as follows:
Trying on the code implementation, we first inherit the AppAsyncBase
class to implement a brand new class AppEventLoop
. Subsequent, override the request_remote
technique and use asyncio.run_coroutine_threadsafe
to name the async_request
technique within the occasion loop. Request technique within the occasion loop. asyncio.run_coroutine_threadsafe
can be thread-safe:
Implement a run_event_loop
technique to name the loop.run_forever
within the thread:
Then, use the contextmanager
decorator to handle the lifecycle of the daemon thread:
Lastly, implement the occasion loop integration and the app launch in the primary technique, and let’s see the consequence:
Good! Click on the button, the standing textual content is modified accordingly, the entire GUI interface runs easily, and IO calls don’t block the GUI ever. Mission completed.