Implementing a basic Thread Pool in Rust.

applied.math.coding
7 min readMay 24, 2022

In one of my previous stories (link), I have given an example implementation for a queue. As queues provide many nice applications we will consider in this story the tempting use of our own one for creating a basic thread pool. In natural conjunction with thread pools queues appear in the form of task queues. That is, we have a queue that is being able to take Tasks and a pool of threads that is being able to process these Tasks in parallel by removing from the queue’s end.

For being able to understand want comes I suggest to have read my article about threads in Rust. If you are a total beginner to Rust you might want to start with a more gentle introduction.

Overview:

A thread pool can be described as a fixed number of threads waiting to do some work. This concept has two apparent advantages. First of all, we avoid the overhead of having to create each time a new thread. Secondly, we ensure the system to take as much as necessary threads but not arbitrarily many. Especially CPU resources are limited and so it makes only sense to run a certain amount of things in parallel.

A thread pool usually comes together with a task queue. This is a queue to which a consumer can add tasks and the pool removes task from this queue in order to process when resources, that is, free threads, are available.

Of course there already exists production-ready implementations of this concept. One of this is included in the famous crate Rayon. The thread pool we are going to implement in this article has to be seen for pure educational purposes only. Though, it is a very good training and touches many aspects of Rust.

Implementation:

Throughout the implementation we make us of the following header:

use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, Sender};
use std::sync::{Arc, Mutex};
use std::thread;
use crate::queue::Queue; // this is our own Queue impl.

So all these objects mentioned there are brought into the current scope.

Let us define a Task:

type Runnable<T> = Box<dyn Fn() -> T + Send>;pub struct Task<T: Send> {
runnable: Runnable<T>…

--

--

applied.math.coding

I am a Software Developer - Java, Rust, SQL, TypeScript - with strong interest doing research in pure and applied Mathematics.