About TaskThread
Never use asyncio.wait or asyncio.gather again
TaskThread is a collection of tools, conventions and a base class that organizes your asyncio python programs seamlessly into hierarchical, well-organized structures.
These structures are analogical to threads and have a thread-like API.
Let’s illustrate what all this is about by an example.
A Sample Problem
Let’s consider a rather complex application which:
Exposes a TCP server
The server accepts various client connections from LAN
Server processes the incoming data and reconstructs data frames from packets received from individual TCP client connections
Processed data from each TCP client is forwarded through websockets to a final destination (say, to cloud)
The processed data is also written into a database
We can see there is a lot of i/o waiting and “multiplexing” going on that can get messy.
When using TaskThread, you start unravelling a problem by identifying hierarchies.
In the present case, a solution, described as a hierarchical list, could look like this:
MasterThread
TCPServerThread
TCPConnectionThread
DataProcessorThread
TCPConnectionThread
DataProcessorThread
...
WebsocketMasterThread
WebSocketSubThread
WebSocketSubThread
...
DatabaseThread
(another kind of hierarchy to solve the problem is, of course, also possible)
MasterThread starts the TCPServerThread which then starts TCPConnectionThread s on demand.
Each TCPConnectionThread starts a DataProcessorThread which reconstructs the packets from indicidual TCPConnectionThread s.
All data flows to upwards in the tree into MasterThread which then passes it onto to WebsocketMasterThread and from there to individual
WebSocketSubThread s. MasterThread passes the data also to DatabaseThread.
Let’s add this data flow to the hierarchical list:
MasterThread
TCPServerThread: UP: data
TCPConnectionThread: UP: data
DataProcessorThread: IN: packets, UP: data
TCPConnectionThread
DataProcessorThread
...
WebsocketMasterThread: IN: data
WebSocketSubThread: IN data
WebSocketSubThread
...
DatabaseThread: IN: data
Here UP designates data going upwards in the tree, while IN shows incoming data at deeper level threads.
Notice that there is not any intercommunication within this tree that is not strictly between a parent and a child.
Threads, how?
So, how to implement such “threads” ? After all this is asyncio, not multithreading!
Well, the entities named here MasterThread, TCPServerThread, etc. are not really “threads”, but collection of asyncio tasks grouped together in a smart way - thus the name TaskThread.
The intercom between the “threads” is done using asyncio queues.
Let’s take a deeper look on the tasks and queues:
DataProcessorThread receives packets (via a listening task) through a queue from TCPConnectionThread. After reconstructing
some data from the packets using a task, DataProcessorThread has a task that sends the dataframes to TCPConnectionThread through another asyncio queue which
TCPConnectionThread is listening with a task, etc.
These queues and listener tasks work seamlessly and are hidden from the TaskThread API user.
At the core of TaskThread philosophy lies rescheduling tasks, i.e. asyncio tasks that reschedule themselves, giving the appearance of “threads” and a thread-like API.
The only thing the API user needs to worry about, is how to initiate, re-schedule and terminate these tasks within their TaskThread implementation.
Advantages
Have you ever run into a situation where you have a complex asyncio program running tons of simultaneous tasks?
For example, you need to run asyncio.wait to “poll” several tasks to see if the tasks have finished or not and then your program’s logic is altered based on that result,
creating an asynchronous mess, maybe even runaway tasks.
Well, you don’t need to touch asyncio.wait or asyncio.gather ever again, after starting to use TaskThread.
Your programs will also become naturally well-organized into threads that have separation of concerns and restricted communication - in accordance with the HIMO principle.
I hope you got all warmed up by now. Exciting, right!?
Next we will take a look at an anatomy of a TaskThread.
Installing
From PyPi simply with:
pip3 install --user task-virtualthread