Last Updated on October 29, 2022
You can get the name of a worker thread in the ThreadPool by calling threading.current_thread() and then accessing the “name” attribute.
Worker threads are named using the default thread naming convention, although have a distinct data type compared to other threads.
In this tutorial, you will discover how to get the name of worker threads in the Python ThreadPool.
Let’s get started.
Need Names of Worker Threads in the ThreadPool
The multiprocessing.pool.ThreadPool in Python provides a pool of reusable threads for executing ad hoc tasks.
A thread pool object which controls a pool of worker threads to which jobs can be submitted.
— multiprocessing — Process-based parallelism
The ThreadPool class extends the Pool class. The Pool class provides a pool of worker processes for process-based concurrency.
Although the ThreadPool class is in the multiprocessing module it offers thread-based concurrency and is best suited to IO-bound tasks, such as reading or writing from sockets or files.
A ThreadPool can be configured when it is created, which will prepare the new threads.
We can issue one-off tasks to the ThreadPool using methods such as apply() or we can apply the same function to an iterable of items using methods such as map().
Results for issued tasks can then be retrieved synchronously, or we can retrieve the result of tasks later by using asynchronous versions of the methods such as apply_async() and map_async().
When using the ThreadPool, we may need the names of the worker threads.
This may be for many reasons, such as:
- To uniquely identify the worker in the application.
- To include the thread name in logging.
- To debug which worker is completing which task.
How can we get the worker thread names in Python?
Run loops using all CPUs, download your FREE book to learn how.
How to Get Worker Thread Names
When working with a ThreadPool, there are two situations where we may want to get the thread name:
- Get the name for each worker thread in the ThreadPool.
- Get the name of the worker completing a given task.
Let’s take a closer look at each approach in turn.
Before we get the name of worker threads, let’s take a brief look at how to get a thread name in general.
How To Get Thread Names
We can get the name of a thread via the name property on the threading.Thread class.
For example:
1 2 3 |
... # report the name of the thread print(thread.name) |
We may acquire an instance of the threading.Thread for the current thread via the threading.current_thread() module function.
For example:
1 2 3 4 5 |
... # get the current thread instance thread = threading.current_thread() # report the name of the thread print(thread.name) |
You can learn more about getting thread names in the tutorial:
How to Get All Worker Thread Names
We can get the name for all workers in the ThreadPool.
This can be achieved from the main thread.
First, we can get a list of all active threads via the threading.enumerate() function. This will return an iterable of threading.Thread instances.
1 2 3 |
... # get all active threads threads = threading.enumerate() |
We can then access the “name” attribute of each.
1 2 3 4 |
... # get the name of each thread for thread in threads: print(thread.name) |
The downside of this approach is that the list of active threads may include threads that are not worker threads.
Another approach to getting the threads name for all worker threads is to configure the ThreadPool to use an initializer function and to get the worker thread name. This function will be called by each worker thread once when it is started in the ThreadPool.
The initialization function does not take any arguments. Within the function, we can get and use the thread instance via the threading.current_thread() function, then get the worker name.
For example:
1 2 3 4 5 6 |
# initialize the worker thread def init_worker(): # get the current worker thread thread = threading.current_thread() # get the name for the current worker thread name = thread.name |
We can then configure the ThreadPool to call the initialization function as each worker is created.
This can be achieved by setting the “initializer” argument in the ThreadPool class constructor to the name of the initialization function.
For example:
1 2 3 |
... # create and configure a thread pool pool = multiprocessing.pool.ThreadPool(initializer=init_worker) |
You can learn more about how to initialize worker threads in the ThreadPool in the tutorial:
How to Get Task Worker Thread Name
We can get the worker name within a given task executed by the ThreadPool.
This can be achieved by calling the threading.current_thread() function within the target task function executed in the ThreadPool to get the thread.
We can then access the “name” attribute directly.
For example:
1 2 3 4 5 6 |
# task executed in a worker thread def task(identifier): # get the current worker thread thread = current_thread() # get the name for the current worker thread name = thread.name |
Now that we know how to get the worker thread name, let’s look at some worked examples.
Get All Worker Thread Names Via Initialization Function
We can explore how to get all worker names using a worker initialization function.
In this example, we will define a worker initialization function that will get and report the name of each worker thread. We will then create and configure a ThreadPool to use the initialization function, then issue many tasks to the ThreadPool in order that all worker threads are started and their names are reported.
First, we must define a worker thread initialization function. The function will not take any arguments and will call the threading.current_thread() function to get the current thread, then access the “name” attribute for the worker thread and report its value.
The init_worker() function below implements this.
1 2 3 4 5 6 |
# initialize the worker thread def init_worker(): # get the current worker thread thread = current_thread() # report the current worker thread name print(f'Worker Name: {thread.name}') |
Next, we can define a task that we will execute in the ThreadPool.
The task will take an integer augment and then block for a fraction of a second.
The task() function below implements this.
1 2 3 4 |
# task executed in a worker thread def task(identifier): # block for a moment sleep(0.5) |
Next, we can create and configure a new ThreadPool.
We will use the context manager interface to ensure the ThreadPool has closed automatically once we are finished with it.
1 2 3 4 |
... # create and configure the thread pool with ThreadPool(initializer=init_worker) as pool: # ... |
You can learn more about the context manager interface in the tutorial:
We will then issue 10 tasks to the ThreadPool, each calling our task() function with an integer between 0 and 9. We will issue the task asynchronously using the map_async() function.
1 2 3 |
... # issues tasks to thread pool result = pool.map_async(task, range(10)) |
Once issued, the main thread will block on the AsyncResult returned from the map_async() function until the issued tasks are complete.
1 2 3 |
... # wait for tasks to complete result.wait() |
You can learn more about the map_async() function in the tutorial:
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# SuperFastPython.com # example of getting worker thread names from time import sleep from threading import current_thread from multiprocessing.pool import ThreadPool # initialize the worker thread def init_worker(): # get the current worker thread thread = current_thread() # report the current worker thread name print(f'Worker Name: {thread.name}') # task executed in a worker thread def task(identifier): # block for a moment sleep(0.5) # protect the entry point if __name__ == '__main__': # create and configure the thread pool with ThreadPool(initializer=init_worker) as pool: # issues tasks to thread pool result = pool.map_async(task, range(10)) # wait for tasks to complete result.wait() # thread pool is closed automatically |
Running the example first configures and creates the ThreadPool.
The ten tasks are then issued to the ThreadPool and the main thread blocks.
Each worker thread is initialized with a call to the init_worker() function. This gets the worker thread instance and then reports the thread name.
In this case, we can see that each worker has a default thread name with the form “Thread-%d“, where “%d” is an integer from 1 to 8 for the worker thread number. You may have more or fewer worker threads depending on the number of logical CPUs in your system.
Each task is then executed, blocking for a fraction of a second and then returning.
All tasks completed and the main thread continues on automatically closing the ThreadPool and then the application itself.
1 2 3 4 5 6 7 8 |
Worker Name: Thread-1 Worker Name: Thread-2 Worker Name: Thread-3 Worker Name: Thread-4 Worker Name: Thread-5 Worker Name: Thread-6 Worker Name: Thread-7 Worker Name: Thread-8 |
This is frustrating as there is nothing unique about the thread names to identify them as being workers in the ThreadPool.
We can update the example and report the type of each thread as well, to see if that is helpful.
This can be achieved using the type() built-in function.
For example:
1 2 3 4 5 6 |
# initialize the worker thread def init_worker(): # get the current worker thread thread = current_thread() # report the current worker thread name print(f'Worker: {type(thread)}, {thread.name}') |
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# SuperFastPython.com # example of getting worker thread types and names from time import sleep from threading import current_thread from multiprocessing.pool import ThreadPool # initialize the worker thread def init_worker(): # get the current worker thread thread = current_thread() # report the current worker thread name print(f'Worker: {type(thread)}, {thread.name}') # task executed in a worker thread def task(identifier): # block for a moment sleep(0.5) # protect the entry point if __name__ == '__main__': # create and configure the thread pool with ThreadPool(initializer=init_worker) as pool: # issues tasks to thread pool result = pool.map_async(task, range(10)) # wait for tasks to complete result.wait() # thread pool is closed automatically |
Running the example creates the ThreadPool and issues the tasks as before.
This time, each task reports both the data type and the name of each thread.
We can see that indeed worker threads in the ThreadPool have a different type, they are instances of the multiprocessing.dummy.DummyProcess class, rather than the expected threading.Thread.
This type along with the thread name may be used to identify workers in the ThreadPool.
1 2 3 4 5 6 7 8 |
Worker: <class 'multiprocessing.dummy.DummyProcess'>, Thread-1 Worker: <class 'multiprocessing.dummy.DummyProcess'>, Thread-2 Worker: <class 'multiprocessing.dummy.DummyProcess'>, Thread-3 Worker: <class 'multiprocessing.dummy.DummyProcess'>, Thread-4 Worker: <class 'multiprocessing.dummy.DummyProcess'>, Thread-5 Worker: <class 'multiprocessing.dummy.DummyProcess'>, Thread-6 Worker: <class 'multiprocessing.dummy.DummyProcess'>, Thread-7 Worker: <class 'multiprocessing.dummy.DummyProcess'>, Thread-8 |
Next, let’s explore getting the worker thread details from within a task.
Free Python ThreadPool Course
Download your FREE ThreadPool PDF cheat sheet and get BONUS access to my free 7-day crash course on the ThreadPool API.
Discover how to use the ThreadPool including how to configure the number of worker threads and how to execute tasks asynchronously
Get Work Thread Names in Task
We can get the worker name within a task executed in the ThreadPool.
In this example, we will get the name of the current thread in the custom task function executed by the ThreadPool. We will then issue a single task to the ThreadPool that reports the name.
Firstly, we can define the custom task function that gets the data type and name for the current worker thread and then reports the value.
The task() function below implements this.
1 2 3 4 5 6 |
# task executed in a worker thread def task(identifier): # get the current worker thread thread = current_thread() # report the current worker thread name print(f'Worker: {type(thread)}, {thread.name}') |
Next, in the main thread, we can create the ThreadPool with a default configuration using the context manager interface.
1 2 3 4 |
... # create and configure the thread pool with ThreadPool() as pool: # ... |
Next, we can issue a single task asynchronously to the ThreadPool using the apply_async() function on the thread pool.
1 2 3 |
... # issues tasks to thread pool result = pool.apply_async(task, (0,)) |
This will return a single AsyncResult object that we can wait on for the issued task to complete.
1 2 3 |
... # wait for tasks to complete result.wait() |
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# SuperFastPython.com # example of getting worker thread name from threading import current_thread from multiprocessing.pool import ThreadPool # task executed in a worker thread def task(identifier): # get the current worker thread thread = current_thread() # report the current worker thread name print(f'Worker: {type(thread)}, {thread.name}') # protect the entry point if __name__ == '__main__': # create and configure the thread pool with ThreadPool() as pool: # issues tasks to thread pool result = pool.apply_async(task, (0,)) # wait for tasks to complete result.wait() # thread pool is closed automatically |
Running the example first creates the ThreadPool.
A single task is issued to the ThreadPool and the main thread blocks until the task is complete.
The task runs, first getting the class data type and name of the current worker thread that is running the task, then reporting the value.
In this case, we can see that the class datatype is the expected DummyProcess, as seen above, and that the thread has the default naming convention of “Thread-%d“, in this case, “Thread-1“.
Note, that the worker thread chosen to execute the task may be different each time the program is run.
The task completes, then the main thread continues on automatically closing the ThreadPool and then terminating the application.
1 |
Worker: <class 'multiprocessing.dummy.DummyProcess'>, Thread-1 |
Overwhelmed by the python concurrency APIs?
Find relief, download my FREE Python Concurrency Mind Maps
Further Reading
This section provides additional resources that you may find helpful.
Books
- Python ThreadPool Jump-Start, Jason Brownlee (my book!)
- Threading API Interview Questions
- ThreadPool PDF Cheat Sheet
I also recommend specific chapters from the following books:
- Python Cookbook, David Beazley and Brian Jones, 2013.
- See: Chapter 12: Concurrency
- Effective Python, Brett Slatkin, 2019.
- See: Chapter 7: Concurrency and Parallelism
- Python in a Nutshell, Alex Martelli, et al., 2017.
- See: Chapter: 14: Threads and Processes
Guides
- Python ThreadPool: The Complete Guide
- Python Multiprocessing Pool: The Complete Guide
- Python ThreadPoolExecutor: The Complete Guide
- Python Threading: The Complete Guide
APIs
References
Takeaways
You now know how to get and report the names of worker threads in the ThreadPool.
Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.
Photo by Antonio Lapa on Unsplash
Do you have any questions?