C++ Concurrent Programming
Overview
C++11 introduced built-in support for multithreading, making concurrent programming more accessible and standardized.
std::thread
Basic Thread Creation
cpp
#include <iostream>
#include <thread>
#include <chrono>
void helloFunction() {
std::cout << "Hello from thread!" << std::endl;
}
void countFunction(int id, int n) {
for (int i = 0; i < n; i++) {
std::cout << "Thread " << id << ": " << i << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
int main() {
// Create and start a thread
std::thread thread1(helloFunction);
// Create thread with parameters
std::thread thread2(countFunction, 1, 5);
std::thread thread3(countFunction, 2, 5);
// Wait for threads to complete
thread1.join();
thread2.join();
thread3.join();
std::cout << "All threads completed!" << std::endl;
return 0;
}Lambda Functions with Threads
cpp
#include <iostream>
#include <thread>
#include <vector>
int main() {
std::vector<std::thread> threads;
// Create multiple threads using lambdas
for (int i = 0; i < 5; i++) {
threads.emplace_back([i]() {
std::cout << "Thread " << i << " is running!" << std::endl;
});
}
// Wait for all threads to complete
for (auto& thread : threads) {
thread.join();
}
std::cout << "All threads completed!" << std::endl;
return 0;
}std::mutex
Mutual Exclusion
cpp
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
int shared_counter = 0;
void incrementCounter(int id) {
for (int i = 0; i < 1000; i++) {
// Lock the mutex before accessing shared data
std::lock_guard<std::mutex> lock(mtx);
shared_counter++;
// Mutex is automatically unlocked when lock goes out of scope
}
}
int main() {
std::thread thread1(incrementCounter, 1);
std::thread thread2(incrementCounter, 2);
thread1.join();
thread2.join();
std::cout << "Final counter value: " << shared_counter << std::endl;
return 0;
}Manual Lock Management
cpp
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
bool data_ready = false;
void producer() {
std::lock_guard<std::mutex> lock(mtx);
std::cout << "Producer: Producing data..." << std::endl;
data_ready = true;
}
void consumer() {
while (true) {
mtx.lock();
if (data_ready) {
std::cout << "Consumer: Consuming data!" << std::endl;
data_ready = false;
mtx.unlock();
break;
}
mtx.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int main() {
std::thread producer_thread(producer);
std::thread consumer_thread(consumer);
producer_thread.join();
consumer_thread.join();
return 0;
}std::condition_variable
Thread Synchronization
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;
bool finished = false;
void producer() {
for (int i = 1; i <= 5; i++) {
{
std::lock_guard<std::mutex> lock(mtx);
data_queue.push(i);
std::cout << "Producer: Produced " << i << std::endl;
}
cv.notify_one(); // Notify one waiting thread
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
{
std::lock_guard<std::mutex> lock(mtx);
finished = true;
}
cv.notify_all(); // Notify all waiting threads
}
void consumer(int id) {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return !data_queue.empty() || finished; });
if (finished && data_queue.empty()) {
break;
}
int value = data_queue.front();
data_queue.pop();
lock.unlock();
std::cout << "Consumer " << id << ": Consumed " << value << std::endl;
}
}
int main() {
std::thread producer_thread(producer);
std::thread consumer_thread1(consumer, 1);
std::thread consumer_thread2(consumer, 2);
producer_thread.join();
consumer_thread1.join();
consumer_thread2.join();
std::cout << "All threads completed!" << std::endl;
return 0;
}std::atomic
Atomic Operations
cpp
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<int> atomic_counter(0);
void incrementAtomic() {
for (int i = 0; i < 1000; i++) {
atomic_counter++; // Atomic operation
}
}
int main() {
std::thread thread1(incrementAtomic);
std::thread thread2(incrementAtomic);
thread1.join();
thread2.join();
std::cout << "Final atomic counter: " << atomic_counter << std::endl;
// Atomic operations with memory ordering
std::atomic<bool> flag(false);
std::thread setter([&flag]() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
flag.store(true, std::memory_order_release);
});
std::thread waiter([&flag]() {
while (!flag.load(std::memory_order_acquire)) {
// Busy wait
}
std::cout << "Flag is now true!" << std::endl;
});
setter.join();
waiter.join();
return 0;
}std::future and std::promise
Asynchronous Programming
cpp
#include <iostream>
#include <future>
#include <chrono>
int calculateSum(int a, int b) {
std::this_thread::sleep_for(std::chrono::seconds(2));
return a + b;
}
int main() {
// Launch async task
std::future<int> result = std::async(std::launch::async, calculateSum, 10, 20);
std::cout << "Task started, waiting for result..." << std::endl;
// Do other work while waiting
std::cout << "Doing other work..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
// Get the result (blocks if not ready)
int sum = result.get();
std::cout << "Result: " << sum << std::endl;
return 0;
}Using std::promise
cpp
#include <iostream>
#include <future>
#include <thread>
void setData(std::promise<int> prom) {
std::this_thread::sleep_for(std::chrono::seconds(2));
prom.set_value(42); // Set the value
}
int main() {
std::promise<int> prom;
std::future<int> fut = prom.get_future();
std::thread thread(setData, std::move(prom));
std::cout << "Waiting for data..." << std::endl;
int value = fut.get(); // Wait for and get the value
std::cout << "Received value: " << value << std::endl;
thread.join();
return 0;
}