C++ Multithreading: thread, mutex and async
C++11 added threads to the standard library. Include <thread>. On Linux, compile with -pthread.
#include <iostream>
#include <thread>
void work(int id) {
std::cout << "worker " << id << " done\n";
}
int main() {
std::thread t1(work, 1);
t1.join(); // wait for the thread
std::thread t2([] { std::cout << "lambda thread done\n"; });
t2.join();
std::cout << "main ends\n";
}
Output
worker 1 done
lambda thread done
main ends
A std::thread must be joined (or detached) before it is destroyed. If not, the program ends with std::terminate. In C++20 use std::jthread, which joins by itself.
Race condition
#include <iostream>
#include <thread>
int counter = 0;
int main() {
auto job = [] { for (int i = 0; i < 100000; i++) counter++; };
std::thread a(job), b(job);
a.join(); b.join();
std::cout << "Expected 200000, result is correct: " << (counter == 200000) << '\n';
}
Output
Expected 200000, result is correct: 0
Two threads changed the same variable without a lock. This is undefined behavior, and on a normal computer the result is usually wrong and changes in every run.
Fix with mutex
#include <iostream>
#include <mutex>
#include <thread>
int counter = 0;
std::mutex m;
int main() {
auto job = [] {
for (int i = 0; i < 100000; i++) {
std::lock_guard<std::mutex> lock(m); // locks here, unlocks at the end of the block
counter++;
}
};
std::thread a(job), b(job);
a.join(); b.join();
std::cout << counter << '\n';
}
Output
200000
Atomic
#include <atomic>
#include <iostream>
#include <thread>
std::atomic<int> counter{0};
int main() {
auto job = [] { for (int i = 0; i < 100000; i++) counter++; };
std::thread a(job), b(job);
a.join(); b.join();
std::cout << counter << '\n';
}
Output
200000
async and future
#include <future>
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<int> v(1000);
std::iota(v.begin(), v.end(), 1); // 1 to 1000
auto first_half = std::async(std::launch::async, [&] { return std::accumulate(v.begin(), v.begin() + 500, 0); });
int second_half = std::accumulate(v.begin() + 500, v.end(), 0);
std::cout << first_half.get() + second_half << '\n'; // get() waits for the result
}
Output
500500
Good practice
- Prefer higher level tools (
std::async, thread pools, parallel algorithms) to raw threads. - Always use
lock_guardorscoped_lock, never lock and unlock by hand. - Use the same order of locks everywhere to avoid a deadlock.
Note:
Use a tool like ThreadSanitizer (-fsanitize=thread) to find data races. Next: C++ Recursion.
Congratulations! Chapter Finished. Learn more about the similar topics:
| Exercises & Assignments |
|---|
| No Content Found. |
| Interview Questions & Answers |
|---|
| No Content Found. |