This project demonstrates the implementation of a custom thread library using POSIX threads (pthread
). The custom library is designed to provide low-level control over thread management and synchronization, offering functionality similar to the C++ Standard Library's <thread>
library. The custom thread library includes various features like mutexes, lock guards, condition variables, thread attributes, and thread-local storage, making it robust and flexible for multithreaded programming.
The custom thread library is designed to work on Unix-like systems that support POSIX threads, such as:
- Linux
- macOS
- BSD variants
It can also be adapted for use on other platforms that support POSIX threads. For Windows, modifications would be necessary to use the native Windows threading API.
-
Thread Creation and Management:
- Create and run multiple threads with specified tasks.
- Join all threads to ensure proper cleanup and synchronization.
-
Mutexes and Lock Guards:
- Protect shared resources from concurrent access by multiple threads.
- Automatic locking and unlocking of mutexes to prevent deadlocks and ensure safe access.
-
Condition Variables:
- Coordinate thread activities by allowing threads to wait for specific conditions to be met.
- Notify one or all waiting threads when conditions change.
-
Thread Attributes:
- Customize thread behavior by setting attributes such as stack size and detach state.
-
Thread-Local Storage (TLS):
- Provide each thread with its own instance of a variable, avoiding conflicts and ensuring thread-specific data is maintained separately.
To evaluate the performance of the custom thread library, we compare it with the standard C++ <thread>
library. The benchmark measures the time taken to create, run, and join a specified number of threads.
Benchmark Code: benchmark.cpp
#include <iostream>
#include <chrono>
#include <thread>
#include <vector>
#include <functional>
#include "ThreadLibrary.h"
// Function to be executed by each thread
void printMessage(int threadId) {
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Simulate work
}
// Function to benchmark the custom thread library
void benchmarkCustomThreadLibrary(int numThreads) {
ThreadLibrary threadLib;
auto start = std::chrono::high_resolution_clock::now();
// Create and run threads
threadLib.createAndRunThreads(numThreads, printMessage);
// Join all threads
threadLib.joinAllThreads();
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
std::cout << "Time taken with custom thread library: " << elapsed_seconds.count() << " seconds" << std::endl;
}
// Function to benchmark the C++ <thread> library
void benchmarkStdThreadLibrary(int numThreads) {
std::vector<std::thread> threads;
auto start = std::chrono::high_resolution_clock::now();
// Create and run threads
for (int i = 0; i < numThreads; ++i) {
threads.emplace_back(printMessage, i);
}
// Join all threads
for (auto& t : threads) {
t.join();
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
std::cout << "Time taken with std::thread library: " << elapsed_seconds.count() << " seconds" << std::endl;
}
int main() {
int numThreads = 1000; // Number of threads to create
std::cout << "Benchmarking with " << numThreads << " threads:" << std::endl;
// Benchmark custom thread library
benchmarkCustomThreadLibrary(numThreads);
// Benchmark std::thread library
benchmarkStdThreadLibrary(numThreads);
return 0;
}
Note: The results may vary as they are highly dependent on the machine, the environment, and other simultaneous processes.
While both the custom thread library and the C++ <thread>
library provide multithreading capabilities, there are several key differences:
-
Low-Level Control:
- The custom thread library provides lower-level control over thread creation and management using POSIX threads, allowing for more fine-grained customization.
-
Custom Features:
- The custom library includes additional features like custom mutexes, condition variables, thread attributes, and thread-local storage, which are designed to mimic and enhance the capabilities of the standard library.
-
Learning and Flexibility:
- Implementing and using the custom thread library offers a deeper understanding of threading concepts and POSIX threads, providing a learning experience and flexibility for specific use cases.
-
Performance:
- The performance can vary based on the specific implementation and use case. Benchmarking helps to evaluate the differences in execution time between the custom library and the
<thread>
library.
- The performance can vary based on the specific implementation and use case. Benchmarking helps to evaluate the differences in execution time between the custom library and the
Functionality:
- Create and run multiple threads by specifying the number of threads and the task each thread should execute.
- Ensure proper synchronization and cleanup by joining all threads.
Example Usage:
ThreadLibrary threadLib;
threadLib.createAndRunThreads(5, printMessage);
threadLib.joinAllThreads();
Functionality:
- Use mutexes to protect shared resources and ensure only one thread accesses a resource at a time.
- Use lock guards to automatically manage mutex locking and unlocking.
Example Usage:
ThreadLibrary::Mutex mtx;
ThreadLibrary::LockGuard lock(mtx);
// Critical section
Functionality:
- Use condition variables to synchronize threads by making threads wait for certain conditions to be met.
- Notify one or all waiting threads when conditions change.
Example Usage:
ThreadLibrary::ConditionVariable cv;
ThreadLibrary::Mutex mtx;
bool ready = false;
void waitFunction() {
ThreadLibrary::LockGuard lock(mtx);
while (!ready) {
cv.wait(mtx);
}
// Proceed after notification
}
void notifyFunction() {
{
ThreadLibrary::LockGuard lock(mtx);
ready = true;
}
cv.notify_all();
}
Functionality:
- Customize thread behavior by setting attributes such as stack size and detach state.
Example Usage:
ThreadLibrary::ThreadAttributes attrs;
attrs.setStackSize(1024 * 1024); // 1 MB stack size
attrs.setDetachState(true); // Detached state
Functionality:
- Provide each thread with its own instance of a variable, ensuring thread-specific data is maintained separately.
Example Usage:
ThreadLibrary::ThreadLocal<int> threadLocalData;
void threadFunction(int threadId) {
threadLocalData.set(threadId);
std::cout << "Thread " << threadId << " local data: " << threadLocalData.get() << std::endl;
}
-
Include the Header File: Make sure to include
ThreadLibrary.h
in your project. -
Compile the Code: Compile the project using a C++ compiler that supports POSIX threads and C++14 or later.
g++ -o main main.cpp ThreadLibrary.cpp -lpthread -std=c++14
-
Run the Executable: After compiling, run the executable to see the output.
./main
The custom thread library provides a robust and flexible alternative to the C++ <thread>
library, with additional features and low-level control. It serves as an excellent learning tool for understanding multithreading concepts and POSIX threads, and it can be customized for specific use cases. By comparing its performance with the standard library through benchmarks, you can evaluate the efficiency and suitability of the custom implementation for your projects.