Basic Thread Use
- threads can be created through the use of
std::thread
class
#include <iostream>
#include <thread>
void printMessage() {
std::cout << "Hello from thread!" << std::endl;
}
int main() {
std::thread t(printMessage); // Create a thread running `printMessage`
t.join(); // Wait for the thread to finish
return 0;
}
- Important Methods:
.join()
: Blocks until the thread finishes execution.detach()
: Makes the thread run independently (not recommended unless necessary)
Common Problems with Threads
- Race Conditions: Occur when two or more threads modify shared memory simultaneously without synchronization
- Deadlocks: Occur when two or more threads wait indefinitely for resources held by each other
Synchronization Mechanisms
Mutex (Mutual Exclusions)
Used to protect shared resources from simultaneous access by multiple threads
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
void printSafe(const std::string& msg) {
mtx.lock(); // Lock the mutex
std::cout << msg << std::endl;
mtx.unlock(); // Unlock the mutex
}
int main() {
std::thread t1(printSafe, "Thread 1");
std::thread t2(printSafe, "Thread 2");
t1.join();
t2.join();
return 0;
}
std::lock_guard
ensures the mutex is released even if an exception occurs
void printSafe(const std::string& msg) {
std::lock_guard<std::mutex> lock(mtx); // Locks and unlocks automatically
std::cout << msg << std::endl;
}
Condition Variables
Allows threads to wait for specific conditions to be met
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void workerThread() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return ready; }); // Wait until `ready` becomes true
std::cout << "Worker thread is running!" << std::endl;
}
int main() {
std::thread worker(workerThread);
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one(); // Notify the worker thread
worker.join();
return 0;
}
Deadlock Prevention
-
A deadlock is where a thread is waiting for an object lock that another thread holds
-
This second thread is waiting for an object that the first thread holds (this problem can scale with several threads)
-
Since each thread is waiting for the other thread to reliquish a lock, they both remain waiting forever (deadlocked)
-
In order for a deadlock to occur, there are 4 conditions that must be met:
- Mutual Exclusion: Only one process can access a resource at a given time
- Hold and Wait: : Processes already holding a resource can request additional resources, without relinquishing their current resources.
- No Preemption: One process cannot forcibly remove another processβ resource.
- Circular Wait: Two or more processes form a circular chain where each process is waiting on another resource in the chain.
- Deadlock prevention involves removing any of the above conditions
- Consistent Lock Ordering: Always lock resources in the same order across threads
- Try Locks: Use
std::try_lock
to avoid blocking indefinetly
if (mtx.try_lock()) {
// Do work
mtx.unlock();
} else {
// Handle lock failure
}
- Scope Locks:
std::scoped_lock
for multiple mutexes
std::scoped_lock lock(mtx1, mtx2); // Prevents deadlocks by locking in one call