C++多线程同步与互斥
在多线程编程中,C++多线程同步与互斥用于控制多个线程对共享资源的访问,以避免数据竞争、死锁等问题。可先参考之前博客 C++多线程运行整理
1. 互斥量(Mutex)
互斥量是一种同步原语,用于防止多个线程同时访问共享资源。当一个线程需要访问共享资源时,它首先需要锁定(lock)互斥量。如果互斥量已经被其他线程锁定,那么请求锁定的线程将被阻塞,直到互斥量被解锁(unlock)。
 std::mutex:用于保护共享资源,防止数据竞争。
std::lock_guard 和 std::unique_lock:自动管理锁的获取和释放
/*
线程管理
join() 用于等待线程完成执行,如果不调用join() 或detach(),而直接销毁线程对象,会导致系统崩溃.
detach() 将线程与主线程分离,线程在后台独立运行,主线程不再等待它.线程同步与互斥
1.互斥量 Muter
如果互斥量已经被其他线程锁定,那么请求锁定的线程将被阻塞,直到互斥量被解锁(unlock)
std::mutex:用于保护共享资源,防止数据竞争
std::lock_guard 和 std::unique_lock:自动管理锁的获取和释放。2.锁 Locks
多种锁类型,用于简化互斥量的使用和管理
std::lock_guard 作用域锁,当构建时自动锁定互斥量,当析构时自动解锁
std::unique_lock 与std::lock_guard 类型,但是提供更多的灵活性(如可转移所有权和手动解锁)3.条件变量 Condition Variable
条件变量用于 线程之间的协调,允许一个或多个线程等待某个条件的发生.通常与互斥量一起使用,以实现线程同步.
std::condition_variable 用于实现线程间的等待和通知机制4. 原子操作(Atomic Operations)
原子操作确保对共享数据的访问是不可分割的,即在多线程环境下,原子操作要么完全执行,要么完全不执行,不会出现中间状态。5. 线程局部存储(Thread Local Storage, TLS)
线程局部存储允许每个线程拥有自己的数据副本。这可以通过thread_local关键字实现,避免了对共享资源的争用。6. 死锁(Deadlock)和避免策略
死锁发生在多个线程互相等待对方释放资源,但没有一个线程能够继续执行。避免死锁的策略包括:
a.总是以相同的顺序请求资源。
b.使用超时来尝试获取资源。
c.使用死锁检测算法。*/
#include <mutex>
#include <iostream>
#include <thread>std::mutex mtx;//全局互斥量void sateFunction(){mtx.lock(); //锁定互斥锁//访问或者修改共享资源mtx.unlock(); //释放互斥所
}int main()
{std::thread t1(sateFunction);std::thread t2(sateFunction);t1.join();t2.join();return 0;
} 
 
//多线程_互斥量#include <iostream>       // for std::cout, std::endl
#include <mutex>            // for std::mutex, std::lock_guard
#include <thread>           // for std::thread, std::this_thread::sleep_for// 全局互斥锁,保护共享资源(如 std::cout)
std::mutex mtx;// 全局共享资源
int shared_counter = 0;/*** @brief 安全的线程函数(使用 RAII 的 lock_guard)*         推荐做法:使用 lock_guard,自动加锁/解锁*/
void safeFunction() {// 使用 RAII:lock_guard 在构造时自动加锁,析构时自动解锁std::lock_guard<std::mutex> lk(mtx);//  此处可以安全访问或修改共享资源++shared_counter;std::cout << " 线程 " << std::this_thread::get_id()<< " 访问共享资源,当前计数: " << shared_counter << std::endl;// 模拟一些工作耗时std::this_thread::sleep_for(std::chrono::milliseconds(50));
}int main() {std::cout << " 主线程开始执行 (ID: " << std::this_thread::get_id() << ")\n\n";// 创建两个线程std::thread t1(safeFunction);std::thread t2(safeFunction);// 等待两个线程完成t1.join();t2.join();std::cout << "\n 所有线程执行完毕,主线程退出。\n";std::cout << " 最终共享计数: " << shared_counter << std::endl;return 0;
}
 
 
2. 锁(Locks)
C++提供了多种锁类型,用于简化互斥量的使用和管理。
 常见的锁类型包括:
 • std::lock_guard:作用域锁,当构造时自动锁定互斥量,当析构时自动解锁。
 • std::unique_lock:与std::lock_guard类似,但提供了更多的灵活性,例如可以转移所有权和手动解锁。
/*
线程管理
join() 用于等待线程完成执行,如果不调用join() 或detach(),而直接销毁线程对象,会导致系统崩溃.
detach() 将线程与主线程分离,线程在后台独立运行,主线程不再等待它.线程同步与互斥
2.锁 Locks
多种锁类型,用于简化互斥量的使用和管理
std::lock_guard 作用域锁,当构建时自动锁定互斥量,当析构时自动解锁
std::unique_lock 与std::lock_guard 类型,但是提供更多的灵活性(如可转移所有权和手动解锁)
*/// #include <mutex>
// #include <thread>// std::mutex mtx;
// void safeFunctionWithLockGuard()
// {
//     std::lock_guard<std::mutex> lk(mtx);
//     // 访问或修改共享资源// }// void safeFunctionWithUniqueLock(){
//     std::unique_lock<std::mutex> ul(mtx);
//     //访问或修改共享资源
//     ul.unlock(); //可选:手动解锁
//     //...
// }// int main()
// {//     return 0;
// }#include <iostream>       // for std::cout, std::endl
#include <mutex>            // for std::mutex, std::lock_guard, std::unique_lock
#include <thread>           // for std::thread, std::this_thread::sleep_for
#include <chrono>           // for std::chrono::milliseconds// 全局互斥锁,保护共享资源
std::mutex mtx;/*** @brief 使用 lock_guard 的安全函数*        lock_guard 是 RAII 风格的互斥锁,构造时自动加锁,*        析构时自动解锁,防止忘记解锁导致死锁。*/
void safeFunctionWithLockGuard() {// 构造 std::lock_guard 时自动调用 mtx.lock()std::lock_guard<std::mutex> lk(mtx);//  此处可以安全访问或修改共享资源std::cout << " [lock_guard] 线程 " << std::this_thread::get_id()<< " 正在访问共享资源...\n";// 模拟一些工作耗时(可选)std::this_thread::sleep_for(std::chrono::milliseconds(100));
}/*** @brief 使用 unique_lock 的安全函数*        unique_lock 比 lock_guard 更灵活,支持:*        - 手动解锁(unlock())*        - 手动加锁(lock())*        - 移动所有权(move-constructible)*        - 与条件变量配合使用(cv.wait())*/
void safeFunctionWithUniqueLock() {// 构造 unique_lock 时自动加锁std::unique_lock<std::mutex> ul(mtx);std::cout << " [unique_lock] 线程 " << std::this_thread::get_id()<< " 正在访问共享资源...\n";// 模拟工作耗时std::this_thread::sleep_for(std::chrono::milliseconds(150));// 可选:手动解锁(例如:想让其他线程短暂访问)ul.unlock();std::cout << " [unique_lock] 手动解锁,允许其他线程访问\n";// 假设在此期间有其他线程尝试访问(可观察输出顺序)std::this_thread::sleep_for(std::chrono::milliseconds(50));//  再次加锁(恢复保护)ul.lock();std::cout << " [unique_lock] 重新加锁,继续操作\n";// 作用域结束,析构时自动解锁(即使未显式调用 unlock)std::cout << " [unique_lock] 线程 " << std::this_thread::get_id()<< " 完成操作,自动解锁\n";
}int main() {std::cout << " 主线程开始执行 (ID: " << std::this_thread::get_id() << ")\n\n";// 创建两个线程,分别调用不同锁函数std::thread t1(safeFunctionWithLockGuard);std::thread t2(safeFunctionWithUniqueLock);// 等待两个线程完成t1.join();t2.join();std::cout << "\n 所有线程执行完毕,主线程退出。\n";return 0;
} 
//多线程_锁(生产者消费者)
#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <functional>// 共享缓冲区(队列)
std::queue<int> buffer;
std::mutex mtx;                    // 保护队列的互斥锁
std::condition_variable not_empty; // 条件变量:当队列非空时通知消费者
std::condition_variable not_full;  // 条件变量:当队列不满时通知生产者// 缓冲区最大容量
const int MAX_SIZE = 5;
bool done = false; // 标志:生产者是否完成/*** @brief 生产者线程函数*        模拟生产数据并放入缓冲区*/
void producer(int id) {for (int i = 1; i <= 10; ++i) {// 模拟生产耗时std::this_thread::sleep_for(std::chrono::milliseconds(200));// 1. 加锁,准备向队列中放入数据std::unique_lock<std::mutex> lk(mtx);// 2. 等待队列未满(避免缓冲区溢出)not_full.wait(lk, []{ return buffer.size() < MAX_SIZE; });// 3. 放入数据buffer.push(i);std::cout << "生产者 " << id << " 生产了数据: " << i << " (当前队列大小: " << buffer.size() << ")\n";// 4. 通知消费者:队列中有数据了not_empty.notify_one();}// 生产结束,通知所有消费者{std::lock_guard<std::mutex> lg(mtx);done = true;}not_empty.notify_all(); // 唤醒所有等待的消费者
}/*** @brief 消费者线程函数*        从缓冲区取出数据并处理*/
void consumer(int id) {while (true) {std::unique_lock<std::mutex> lk(mtx);// 1. 等待队列非空,或生产者已结束not_empty.wait(lk, []{ return !buffer.empty() || done; });// 2. 如果队列为空且生产者已完成,则退出if (buffer.empty() && done) {std::cout << "消费者 " << id << " 收到结束信号,停止工作。\n";break;}// 3. 取出数据int data = buffer.front();buffer.pop();std::cout << "消费者 " << id << " 消费了数据: " << data << " (剩余队列大小: " << buffer.size() << ")\n";// 4. 通知生产者:队列有空位了not_full.notify_one();}
}int main() {std::cout << "=== 生产者-消费者模型开始运行 ===\n";std::cout << "缓冲区最大容量: " << MAX_SIZE << "\n";std::cout << "生产者线程数: 2\n";std::cout << "消费者线程数: 3\n\n";// 创建线程std::thread prod1(producer, 1);std::thread prod2(producer, 2);std::thread cons1(consumer, 1);std::thread cons2(consumer, 2);std::thread cons3(consumer, 3);// 等待所有线程完成prod1.join();prod2.join();cons1.join();cons2.join();cons3.join();std::cout << "\n=== 生产者-消费者模型执行完毕 ===\n";return 0;
} 
 
3. 条件变量(Condition Variable)
条件变量用于线程间的协调,允许一个或多个线程等待某个条件的发生。它通常与互斥量一起使用,以实现线程间的同步。
 std::condition_variable用于实现线程间的等待和通知机制。
/*
3.条件变量 Condition Variable
条件变量 用于线程间的协调,允许一个或者多个线程等待某个条件的发生.通常与互斥量一起使用,以实现线程间的同步.
std::condition_variable用于实现线程间的等待和通知机制。
std::condition_variable 必须在 std::unique_lock 作用域内使用
*/#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> lk(mtx);cv.wait(lk, []{ return ready; }); // 等待条件满足std::cout << "工作线程:数据已准备,开始执行...\n";// 模拟工作std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "工作线程:任务完成!\n";
}void mainThread()
{std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟准备时间{std::lock_guard<std::mutex> lg(mtx);std::cout << "主线程:数据准备完成,设置 ready = true\n";ready = true;} // lock_guard 释放锁cv.notify_one(); // 通知一个等待的线程std::cout << "主线程:已通知工作线程!\n";
}int main()
{std::thread worker(workerThread);std::thread main_t(mainThread);worker.join();main_t.join();return 0;
} 
 
4. 原子操作(Atomic Operations)
原子操作确保对共享数据的访问是不可分割的,即在多线程环境下,原子操作要么完全执行,要么完全不执行,不会出现中间状态。
/*
4.原子操作 Atomic Operations
原子操作确保对共享数据的访问是不可分割的,即多线程环境下,原子操作只能完全执行或完全不执行.
*/#include <atomic>
#include <thread>
#include <iostream>  
std::atomic<int> count(0);//声明一个共享变量 countvoid increment() {count.fetch_add(1, std::memory_order_relaxed);
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();// 使用 count.load() 显式获取值std::cout << "多线程原子操作: " << count << std::endl;std::cout << "多线程原子操作: " << count.load() << std::endl;return 0; // 显式返回值,更清晰
}
 
//多线程_原子操作#include <atomic>
#include <thread>
#include <iostream>
#include <mutex>std::atomic<int> count{0};
std::mutex cout_mutex;void increment() {count.fetch_add(1, std::memory_order_relaxed);std::lock_guard<std::mutex> lk(cout_mutex);std::cout << "线程 " << std::this_thread::get_id()<< ",计数: " << count.load() << '\n';
}int main() {std::cout << "主线程开始 (ID: " << std::this_thread::get_id() << ")\n";std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "\n操作完成\n";std::cout << "最终计数: " << count.load() << '\n';return 0;
} 
 
5. 线程局部存储(Thread Local Storage, TLS)
线程局部存储允许每个线程拥有自己的数据副本。这可以通过thread_local关键字实现,避免了对共享资源的争用。
/*
5.线程局部存储 Tread Local Storage(TLS)
线程局部存储 允许每个线程拥有自己的数据副本,也可以通过 thread_local关键字来实现,避免对共享资源的争用.
*/#include <iostream>
#include <thread>thread_local int threadData = 0;
thread_local int threadData1 = 0;void threadFunction()
{threadData = 42;//每个线程都有自己的threadData副本std::cout << "线程数据:" << threadData << std::endl; 
}void threadFunction1()
{threadData1 = 53;//每个线程都有自己的threadData副本std::cout << "线程数据:" << threadData1 << std::endl; 
}int main()
{std::thread t1(threadFunction);//线程42t1.join();std::thread t2(threadFunction1);//线程53t2.join();return 0;
} 
 
6. 死锁(Deadlock)和避免策略
死锁发生在多个线程互相等待对方释放资源,但没有一个线程能够继续执行。避免死锁的策略包括:
 a.总是以相同的顺序请求资源。
 b.使用超时来尝试获取资源。
 c.使用死锁检测算法。
/*
6. 死锁(Deadlock)和避免策略
死锁发生在多个线程互相等待对方释放资源,但没有一个线程能够继续执行。避免死锁的策略包括:
a.总是以相同的顺序请求资源。
b.使用超时来尝试获取资源。
c.使用死锁检测算法。
*/#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>// 使用 timed_mutex 替代 mutex,支持超时机制
std::timed_mutex account1, account2;//  演示死锁:两个线程以不同顺序请求锁
void transfer_with_deadlock(int amount) {std::cout << "线程 " << std::this_thread::get_id()<< " 尝试转账 " << amount << " 元\n";account1.lock();std::this_thread::sleep_for(std::chrono::milliseconds(100));account2.lock();std::cout << "转账成功:从 account1 转 " << amount<< " 到 account2\n";account2.unlock();account1.unlock();
}//  策略 a:总是以固定顺序获取锁(避免死锁)
void transfer_with_ordering(int amount) {std::cout << "线程 " << std::this_thread::get_id()<< " 使用固定顺序锁尝试转账 " << amount << " 元\n";// 固定顺序:先获取 account1,再获取 account2account1.lock();std::this_thread::sleep_for(std::chrono::milliseconds(50));account2.lock();std::cout << "转账成功:从 account1 转 " << amount<< " 到 account2(固定顺序)\n";account2.unlock();account1.unlock();
}//  策略 b:使用超时机制尝试获取锁
bool try_transfer_with_timeout(int amount, int timeout_ms) {std::cout << "线程 " << std::this_thread::get_id()<< " 使用超时尝试转账 " << amount << " 元\n";// 尝试获取 account1,超时 150msauto deadline = std::chrono::steady_clock::now()+ std::chrono::milliseconds(timeout_ms);if (!account1.try_lock_until(deadline)) {std::cout << "获取 account1 锁失败(超时),放弃本次尝试\n";return false;}std::this_thread::sleep_for(std::chrono::milliseconds(50));// 尝试获取 account2,超时 150msif (!account2.try_lock_until(deadline)) {std::cout << "获取 account2 锁失败(超时),释放 account1 锁并放弃\n";account1.unlock();return false;}std::cout << "转账成功:从 account1 转 " << amount<< " 到 account2(使用超时机制)\n";account2.unlock();account1.unlock();return true;
}//  策略 c:简化死锁检测(模拟)
void transfer_with_deadlock_detection() {std::cout << "线程 " << std::this_thread::get_id()<< " 使用死锁检测机制尝试转账\n";// 尝试获取 account1 锁,最大等待 100msif (account1.try_lock_for(std::chrono::milliseconds(100))) {std::cout << "成功获取 account1 锁\n";// 尝试获取 account2 锁,最大等待 100msif (account2.try_lock_for(std::chrono::milliseconds(100))) {std::cout << "成功获取 account2 锁,转账成功\n";account2.unlock();} else {std::cout << "无法获取 account2 锁,可能死锁风险!\n";account1.unlock();std::cout << "释放 account1 锁,避免死锁\n";}} else {std::cout << "无法获取 account1 锁,放弃尝试\n";}
}int main() {std::cout << "=== 死锁演示与三种避免策略 ===\n\n";// 测试 1:死锁演示std::cout << "1. 演示死锁:两个线程以不同顺序请求锁\n";std::thread t1([&]() { transfer_with_deadlock(100); });std::thread t2([&]() { transfer_with_deadlock(200); });t1.join();t2.join();std::cout << "\n" << std::string(60, '-') << "\n\n";// 测试 2:策略 a —— 固定顺序获取锁std::cout << "2. 策略 a:固定顺序获取锁(避免死锁)\n";std::thread t3([&]() { transfer_with_ordering(150); });std::thread t4([&]() { transfer_with_ordering(250); });t3.join();t4.join();std::cout << "\n" << std::string(60, '-') << "\n\n";// 测试 3:策略 b —— 使用超时机制std::cout << "3. 策略 b:使用超时机制尝试获取锁\n";std::thread t5([&]() { try_transfer_with_timeout(100, 150); });std::thread t6([&]() { try_transfer_with_timeout(200, 150); });t5.join();t6.join();std::cout << "\n" << std::string(60, '-') << "\n\n";// 测试 4:策略 c —— 简化死锁检测std::cout << "4. 策略 c:使用死锁检测机制(模拟)\n";std::thread t7(transfer_with_deadlock_detection);t7.join();std::cout << "\n=== 所有测试完成 ===\n";return 0;
} 
 
补充:线程间通信
std::future 和 std::promise:实现线程间的值传递。
//使用 std::future 和 std::promise 实现线程通信
// 一个线程计算结果,另一个线程获取结果#include <iostream>
#include <thread>
#include <future>
#include <vector>int compute_data(int value) {// 模拟耗时计算std::this_thread::sleep_for(std::chrono::milliseconds(500));return value * 2;
}int main() {// 创建 promise 和 futurestd::promise<int> result_promise;std::future<int> result_future = result_promise.get_future();// 启动计算线程std::thread worker([&result_promise](int input) {int output = compute_data(input);result_promise.set_value(output);  // 发送结果}, 10);// 主线程等待结果std::cout << "主线程:等待结果..." << std::endl;int result = result_future.get();  // 获取结果(阻塞)std::cout << "主线程:接收结果:" << result << std::endl;// 等待工作线程结束worker.join();return 0;
} 
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>// 全局共享数据:一个线程安全的队列
std::queue<int> data_queue;
std::mutex queue_mutex;
std::condition_variable queue_condition;// 生产者线程函数
void producer(int id, int count) {for (int i = 0; i < count; ++i) {int value = id * 100 + i;// 加锁,写入数据std::lock_guard<std::mutex> lock(queue_mutex);data_queue.push(value);std::cout << "生产者" << id << " produced: " << value << std::endl;// 通知消费者有新数据queue_condition.notify_one();}
}// 消费者线程函数
void consumer(int id) {while (true) {std::unique_lock<std::mutex> lock(queue_mutex);// 等待队列中有数据,或无数据时阻塞queue_condition.wait(lock, [] { return !data_queue.empty(); });// 取出数据int value = data_queue.front();data_queue.pop();lock.unlock(); // 释放锁// 处理数据std::cout << "消费者" << id << " consumed: " << value << std::endl;// 模拟处理耗时std::this_thread::sleep_for(std::chrono::milliseconds(100));}
}int main() {// 创建多个生产者和消费者线程std::vector<std::thread> producers;std::vector<std::thread> consumers;// 启动 2 个生产者线程,每个生产 5 个数据for (int i = 0; i < 2; ++i) {producers.emplace_back(producer, i, 5);}// 启动 2 个消费者线程for (int i = 0; i < 2; ++i) {consumers.emplace_back(consumer, i);}// 等待生产者完成任务for (auto& t : producers) {t.join();}// 通知消费者停止(由于消费者使用无限循环,只能通过外部控制)// 为了安全终止,可以添加一个标志位,这里简化处理std::cout << "所有生产者已完成,等待消费者完成..." << std::endl;// 等待消费者线程(实际不会退出,除非手动终止)// 在真实场景中,应使用共享标志(如 atomic<bool>)来控制退出for (auto& t : consumers) {t.join();}return 0;
}
 
整理不易,诚望各位看官点赞 收藏 评论 予以支持,这将成为我持续更新的动力源泉。若您在阅览时存有异议或建议,敬请留言指正批评,让我们携手共同学习,共同进取,吾辈自当相互勉励!
