Thread Fundamentals
A thread represents the smallest executable unit managed by the operating system scheduler. Unlike processes, threads within the same process share memory space, file descriptors, and other resources. This model enables efficient parallel execution on multi-core systems but requires careful synchronization to avoid data races.
Linux supports two threading models:
- User-Level Threads (ULT): Managed entirely by user-space libraries without kernel involvement. They offer fast context switching but cannot utilize multiple cores simultaneously.
- Kernel-Level Threads (KLT): Directly managed by the OS kernel, allowing true parallel execution across processor cores.
Thread Creation
POSIX Threads (pthreads)
The pthread library provides a standardized API for thread operations.
#include <iostream>
#include <pthread.h>
void* worker_routine(void* data) {
int tid = *static_cast<int*>(data);
std::cout << "Worker ID: " << tid << " active\n";
return nullptr;
}
int main() {
pthread_t thread_handle;
int identifier = 42;
pthread_create(&thread_handle, nullptr, worker_routine, &identifier);
pthread_join(thread_handle, nullptr);
return 0;
}
C++11 Standard Library
The <thread> header offers a higher-level abstraction.
#include <iostream>
#include <thread>
void task_entry(int id) {
std::cout << "Task ID: " << id << " executing\n";
}
int main() {
int param = 100;
std::thread worker(task_entry, param);
worker.join();
return 0;
}
Thread Lifecycle Management
Identification and Attributes
Each thread possesses a unique identifier accessible during execution.
#include <iostream>
#include <thread>
void executor() {
std::cout << "Current thread ID: " << std::this_thread::get_id() << "\n";
}
int main() {
std::thread worker(executor);
std::cout << "Created thread ID: " << worker.get_id() << "\n";
worker.join();
return 0;
}
State Transitions
Proper handling of thread states ensures resource efficiency.
#include <iostream>
#include <thread>
#include <chrono>
void job_processor() {
std::cout << "Job started\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Job finished\n";
}
int main() {
std::thread worker(job_processor);
if (worker.joinable()) {
worker.join();
}
return 0;
}
Synchronization Mechanisms
Mutual Exclusion
Protect shared resources using mutexes and condition variables.
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex lock_mutex;
std::condition_variable signal_cv;
bool flag_ready = false;
void background_task() {
std::unique_lock<std::mutex> guard(lock_mutex);
signal_cv.wait(guard, []{ return flag_ready; });
std::cout << "Signal received, processing data\n";
}
int main() {
std::thread processor(background_task);
std::this_thread::sleep_for(std::chrono::milliseconds(200));
{
std::lock_guard<std::mutex> guard(lock_mutex);
flag_ready = true;
}
signal_cv.notify_one();
processor.join();
return 0;
}
Counting Semaphores
Control access to limited resources using semaphore patterns.
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
class CountingSemaphore {
public:
explicit CountingSemaphore(int initial_count) : counter(initial_count) {}
void acquire() {
std::unique_lock<std::mutex> lock(sync_mutex);
wakeup_signal.wait(lock, [this] { return counter > 0; });
--counter;
}
void release() {
std::lock_guard<std::mutex> lock(sync_mutex);
++counter;
wakeup_signal.notify_one();
}
private:
std::mutex sync_mutex;
std::condition_variable wakeup_signal;
int counter;
};
CountingSemaphore semaphore(2);
void processor(int id) {
semaphore.acquire();
std::cout << "Processor " << id << " acquired resource\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
semaphore.release();
}
int main() {
std::thread p1(processor, 1);
std::thread p2(processor, 2);
std::thread p3(processor, 3);
p1.join(); p2.join(); p3.join();
return 0;
}
Thread Pool Pattern
Reuse threads for task execution efficiency.
#include <iostream>
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
class TaskExecutor {
public:
explicit TaskExecutor(size_t worker_count) : terminate_flag(false) {
for (size_t i = 0; i < worker_count; ++i) {
workers.emplace_back([this] {
while (true) {
std::function<void()> current_job;
{
std::unique_lock<std::mutex> lock(queue_lock);
wakeup_signal.wait(lock, [this] {
return terminate_flag || !job_queue.empty();
});
if (terminate_flag && job_queue.empty()) return;
current_job = std::move(job_queue.front());
job_queue.pop();
}
current_job();
}
});
}
}
template<typename Callable>
void submit(Callable&& task) {
{
std::lock_guard<std::mutex> lock(queue_lock);
job_queue.emplace(std::forward<Callable>(task));
}
wakeup_signal.notify_one();
}
~TaskExecutor() {
{
std::lock_guard<std::mutex> lock(queue_lock);
terminate_flag = true;
}
wakeup_signal.notify_all();
for (auto& worker : workers) {
worker.join();
}
}
private:
std::vector<std::thread> workers;
std::queue<std::function<void()>> job_queue;
std::mutex queue_lock;
std::condition_variable wakeup_signal;
bool terminate_flag;
};