A lightweight, easy-to-use thread pool implementation for Free Pascal. Simplify parallel processing for simple tasks! ⚡
Important
Parallel processing can improve performance for CPU-intensive tasks that can be executed independently. However, not all tasks benefit from parallelization. See Thread Management for important considerations.
Note
This library is an experimental project, as I was exploring the concept of thread pools and how to implement them in Free Pascal.
Hence, this library is not suitable for high-load applications. It is designed for simple parallel processing tasks that can be executed in parallel.
Tip
If you are looking for performant and battle-tested threading libraries, please check out these alternatives:
- Mormot2 Threading Library by @synopse
- ezthreads by @mr-highball
- OmniThreadLibrary by @gabr42 (Delphi-only)
- Or use threading in other languages via DLL/EXE calls;
- Go lang with Goroutines
- Python with concurrent.futures
- Rust with threadpool
- Any other language that supports modern threading
- 🚀 ThreadPool for Free Pascal
This library provides two thread pool implementations, each with its own strengths:
uses ThreadPool.Simple;
- Global singleton instance for quick use
- Direct task execution
- Automatic thread count management
- Best for simple parallel tasks
- Lower memory overhead
uses ThreadPool.ProducerConsumer;
A thread pool with fixed-size circular buffer (1024 items) and built-in backpressure handling:
-
Queue Management
- Fixed-size circular buffer for predictable memory usage
- Efficient space reuse without dynamic resizing
- Configurable capacity (default: 1024 items)
-
Backpressure Handling
- Load-based adaptive delays (10ms to 100ms)
- Automatic retry mechanism (up to 5 attempts)
- Throws EQueueFullException when retries exhausted
-
Monitoring & Debug
- Thread-safe error capture with thread IDs
- Detailed debug logging (can be disabled)
Warning
While the system includes automatic retry mechanisms, it's recommended that users implement their own error handling strategies for scenarios where the queue remains full after all retry attempts.
-
Thread Count Management
- Minimum 4 threads for optimal parallelism
- Maximum 2×
ProcessorCount
to prevent overload - Fixed count after initialization
-
Task Types Support
- Simple procedures:
Pool.Queue(@MyProc)
- Object methods:
Pool.Queue(@MyObject.MyMethod)
- Indexed variants:
Pool.Queue(@MyProc, Index)
- Simple procedures:
-
Thread Safety
- Built-in synchronization
- Safe resource sharing
- Protected error handling
-
Error Management
- Thread-specific error capture
- Error messages with thread IDs
- Continuous operation after exceptions
Note
Thread count is determined by TThread.ProcessorCount
at startup and remains fixed. See Thread Management for details.
uses ThreadPool.Simple;
// Simple parallel processing
procedure ProcessItem(index: Integer);
begin
WriteLn('Processing item: ', index);
end;
begin
// Queue multiple items
for i := 1 to 5 do
GlobalThreadPool.Queue(@ProcessItem, i);
GlobalThreadPool.WaitForAll;
end;
uses ThreadPool.ProducerConsumer;
procedure DoWork;
begin
WriteLn('Working in thread: ', GetCurrentThreadId);
end;
var
Pool: TProducerConsumerThreadPool;
begin
Pool := TProducerConsumerThreadPool.Create;
try
Pool.Queue(@DoWork);
Pool.WaitForAll;
finally
Pool.Free;
end;
end;
program ErrorHandling;
{$mode objfpc}{$H+}{$J-}
uses
Classes, SysUtils, ThreadPool.Simple;
procedure RiskyProcedure;
begin
raise Exception.Create('Something went wrong!');
end;
var
Pool: TSimpleThreadPool;
begin
Pool := TSimpleThreadPool.Create(4); // Create with 4 threads
try
Pool.Queue(@RiskyProcedure);
Pool.WaitForAll;
// Check for errors after completion
if Pool.LastError <> '' then
begin
WriteLn('An error occurred: ', Pool.LastError);
Pool.ClearLastError; // Clear for reuse if needed
end;
finally
Pool.Free;
end;
end.
program ErrorHandling;
{$mode objfpc}{$H+}{$J-}
uses
Classes, SysUtils, ThreadPool.ProducerConsumer;
procedure RiskyProcedure;
begin
raise Exception.Create('Something went wrong!');
end;
var
Pool: TProducerConsumerThreadPool;
begin
Pool := TProducerConsumerThreadPool.Create;
try
try
Pool.Queue(@RiskyProcedure);
except
on E: EQueueFullException do
WriteLn('Queue is full after retries: ', E.Message);
end;
Pool.WaitForAll;
// Check for errors after completion
if Pool.LastError <> '' then
begin
WriteLn('An error occurred: ', Pool.LastError);
Pool.ClearLastError; // Clear for reuse if needed
end;
finally
Pool.Free;
end;
end.
Note
Error Handling
- 🛡️ Exceptions are caught and stored with thread IDs
- ⚡ Pool continues operating after exceptions
- 🔄 Use ClearLastError to reset error state
Debugging
- 🔍 Error messages contain thread identification
- 📝 Debug logging enabled by default (configurable)
- 📊 Queue capacity monitoring available
Use Simple Thread Pool when:
- Direct task execution without queuing needed
- Task count is predictable and moderate
- Low memory overhead is important
- Global instance (GlobalThreadPool) convenience desired
- Simple error handling is sufficient
Use Producer-Consumer Pool when:
- High volume of tasks with rate control needed
- Backpressure handling required
- Queue overflow protection important
- Need detailed execution monitoring
- Want configurable retry mechanisms
-
🎓 Simple Demo (
examples/SimpleDemo/SimpleDemo.lpr
)- Basic usage with GlobalThreadPool
- Demonstrates procedures and methods
- Shows proper object lifetime
-
🔢 Thread Pool Demo (
examples/SimpleThreadpoolDemo/SimpleThreadpoolDemo.lpr
)- Custom thread pool management
- Thread-safe operations
- Error handling patterns
-
📝 Word Counter (
examples/SimpleWordCounter/SimpleWordCounter.lpr
)- Queue-based task processing
- Thread-safe counters
- File I/O with queue management
-
🔢 Square Numbers (
examples/SimpleSquareNumbers/SimpleSquareNumbers.lpr
)- High volume task processing
- Queue full handling
- Performance comparison
-
🎓 Simple Demo (
examples/ProdConSimpleDemo/ProdConSimpleDemo.lpr
)- Basic usage with ProducerConsumerThreadPool
- Demonstrates procedures and methods
- Shows proper object lifetime
-
🔢 Square Numbers (
examples/ProdConSquareNumbers/ProdConSquareNumbers.lpr
)- High volume task processing
- Queue full handling
- Backpressure demonstration
- Performance monitoring
-
📝 Message Processor (
examples/ProdConMessageProcessor/ProdConMessageProcessor.lpr
)- Queue-based task processing
- Thread-safe message handling
- Graceful shutdown
- Error handling patterns
-
Add the
src
directory to your project's search path -
Choose your implementation:
For Simple Thread Pool:
uses ThreadPool.Simple;
For Producer-Consumer Thread Pool:
uses ThreadPool.ProducerConsumer;
-
Start using:
- Simple: Use
GlobalThreadPool
or createTSimpleThreadPool
- Producer-Consumer: Create
TProducerConsumerThreadPool
- Simple: Use
- 💻 Free Pascal 3.2.2 or later
- 📦 Lazarus 3.6.0 or later
- 🆓 No external dependencies
- ThreadPool.Simple API Documentation
- ThreadPool.Simple Technical Details
- ThreadPool.ProducerConsumer API Documentation
- ThreadPool.ProducerConsumer Technical Details
- Go to the
tests/
directory - Open
TestRunner.lpi
in Lazarus IDE and compile - Run
./TestRunner.exe -a -p --format=plain
to see the test results. - Ensure all tests pass to verify the library's functionality
May take up to 5 mins to run all tests.
- Default: Uses ProcessorCount when thread count ≤ 0
- Minimum: 4 threads enforced
- Maximum: 2× ProcessorCount
- Fixed after creation (no dynamic scaling)
Simple Thread Pool
- Direct task execution without queuing
- Continuous task processing
- Clean shutdown handling
Producer-Consumer Thread Pool
- Fixed-size circular queue (1024 items by default, configurable)
- Backpressure handling with adaptive delays
- Graceful overflow management
- Adaptive thread adjustment based on a load factor
- Support for
procedure Queue(AMethod: TProc; AArgs: array of Const);
- More comprehensive tests
- More examples
Special thanks to the Free Pascal and Lazarus communities and the creators of the threading libraries mentioned above for inspiration!
This project is licensed under the MIT License - see the LICENSE file for details.
💡 More Tip: Check out the examples directory for more usage patterns!