Lesson 25 • Advanced
Concurrency in C++
Write concurrent programs with std::thread, async, future, promise, and the producer-consumer pattern.
What You'll Learn
- ✓ Creating and joining threads
- ✓ async, future, and promise
- ✓ Condition variables for thread signalling
- ✓ Producer-consumer pattern
Why Concurrency?
Modern CPUs have multiple cores. A single-threaded program uses only one core — wasting 75-93% of your CPU. Concurrency lets you run multiple tasks simultaneously, dramatically speeding up computation-heavy and I/O-bound work.
C++11 introduced a portable threading library: std::thread, std::async, std::future, and synchronisation primitives. No more platform-specific pthread or Windows Thread APIs.
Threads & Lambdas
Create threads, pass arguments, and join them
#include <iostream>
#include <thread>
#include <vector>
using namespace std;
// Simple thread function
void greet(const string& name, int times) {
for (int i = 0; i < times; i++) {
cout << "Hello from " << name << " (iteration " << i << ")" << endl;
}
}
// Using lambda with threads
void lambdaThreads() {
cout << "\n=== Lambda Threads ===" << endl;
int result1 = 0, result2 = 0;
thread t1([&result1]() {
for (int i = 0; i < 1000; i++) result1 += i;
...async, future & promise
Launch async tasks and retrieve results with futures
#include <iostream>
#include <future>
#include <chrono>
#include <vector>
#include <numeric>
using namespace std;
// Simulate expensive computation
long long computeSum(long long start, long long end) {
long long sum = 0;
for (long long i = start; i < end; i++) {
sum += i;
}
return sum;
}
// async — fire and forget, get result later
void asyncExample() {
cout << "=== async & future ===" << endl;
auto start = chrono::high_resolution_clock::now();
//
...Producer-Consumer Pattern
Build a thread-safe message queue with condition variables
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
using namespace std;
// Producer-Consumer with condition variable
class MessageQueue {
queue<string> messages;
mutex mtx;
condition_variable cv;
bool done = false;
public:
void send(const string& msg) {
{
lock_guard<mutex> lock(mtx);
messages.push(msg);
cout << "[Producer] Sent: " << msg << endl;
}
cv.notify_on
...Common Mistakes
⚠️ Forgetting to join: If a std::thread is destroyed without join() or detach(), the program calls std::terminate.
⚠️ Data races: Two threads writing to the same variable without synchronisation is undefined behaviour. Always use mutexes or atomics.
⚠️ Dangling references: Passing references to local variables in threads — the variable may be destroyed before the thread reads it.
Pro Tips
💡 Prefer async over thread: std::async handles thread lifecycle and exception propagation automatically.
💡 Use jthread (C++20): std::jthread auto-joins on destruction — no more forgotten joins.
💡 Avoid shared mutable state: The safest concurrent code has no shared data. Pass data in and collect results out.
📋 Quick Reference
| Concept | Syntax |
|---|---|
| Create thread | thread t(func, args...); |
| Wait for thread | t.join(); |
| Async task | auto f = async(launch::async, func); |
| Get result | auto val = f.get(); |
| Set promise | prom.set_value(val); |
Lesson Complete!
You can now write concurrent C++ programs with threads, async, and message queues. Next: Mutexes & Locks — protecting shared data and avoiding deadlocks.
Sign up for free to track which lessons you've completed and get learning reminders.