线程同步:互斥与条件变量

简介: 线程同步:互斥与条件变量

互斥量和条件变量:深入解析与代码实现

在并发编程中,互斥量和条件变量是两种非常重要的同步机制,它们分别用于保护共享资源不被多个线程同时访问,以及协调线程之间的执行顺序。本文将深入探讨互斥量和条件变量的工作原理,并通过代码示例展示如何在实际开发中使用它们。


一、互斥量(Mutex)

互斥量,也被称为互斥锁或互斥对象,是一种常用的同步原语,用于保护共享资源的访问。当一个线程获得互斥量的锁时,其他尝试获取该锁的线程将被阻塞,直到锁被释放。

在C++中,std::mutex是一个标准的互斥量实现。下面是一个简单的示例,展示了如何使用std::mutex来保护一个共享计数器:

#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
std::mutex mtx; // 全局互斥量
int counter = 0; // 共享计数器
void increment(int id) {
mtx.lock(); // 请求互斥量的锁
++counter; // 修改共享资源
std::cout << "Thread " << id << " incremented counter to " << counter << std::endl;
mtx.unlock(); // 释放互斥量的锁
}
int main() {
std::vector<std::thread> threads;
const int num_threads = 10;
// 创建多个线程,每个线程都会调用increment函数
for (int i = 0; i < num_threads; ++i) {
threads.push_back(std::thread(increment, i));
}
// 等待所有线程完成
for (auto& th : threads) {
th.join();
}
std::cout << "Final counter value is " << counter << std::endl;
return 0;
}

在这个例子中,每个线程都会调用increment函数来增加共享计数器counter的值。通过使用mtx.lock()和mtx.unlock()来包裹对counter的访问,我们确保了每次只有一个线程能够修改counter的值。如果没有互斥量的保护,多个线程可能会同时修改counter,导致数据竞争和不一致的结果。


二、条件变量(Condition Variable

条件变量用于线程间的同步,允许一个或多个线程等待某个条件成立(通常由另一个线程触发)。条件变量通常与互斥量一起使用,以确保在检查条件或修改共享数据时不会发生数据竞争。

在C++中,std::condition_variable是一个标准的条件变量实现。下面是一个使用条件变量的示例,其中有一个生产者线程生产数据,一个消费者线程消费数据:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::queue<int> data_queue; // 数据队列
std::mutex mtx; // 互斥量,保护数据队列
std::condition_variable cond_var; // 条件变量
bool stop = false; // 停止标志
void producer() {
for (int i = 0; i < 10; ++i) {
std::unique_lock<std::mutex> lock(mtx);
data_queue.push(i);
lock.unlock();
cond_var.notify_one(); // 通知一个等待的线程
std::this_thread::sleep_for(std::chrono::seconds(1));
}
{
std::lock_guard<std::mutex> lock(mtx);
stop = true;
}
cond_var.notify_all(); // 通知所有等待的线程
}
void consumer() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cond_var.wait(lock, [] { return !data_queue.empty() || stop; }); // 等待数据可用或收到停止信号
if (stop && data_queue.empty()) {
break; // 所有数据已消费且收到停止信号,退出循环
}
int data = data_queue.front();
data_queue.pop();
lock.unlock();
std::cout << "Consumed: " << data << std::endl;
}
}
int main() {
std::thread producer_thread(producer);
std::thread consumer_thread(consumer);
producer_thread.join();
consumer_thread.join();
return 0;
}


在这个例子中,生产者线程producer将整数推送到data_queue中,然后通知条件变量cond_var。消费者线程consumer在一个循环中等待条件变量cond_var,直到队列非空或有停止信号为止。当队列非空时,消费者线程将取出数据并处理它。当所有数据都被消费且收到停止信号时,消费者线程将退出循环。

值得注意的是,我们在使用cond_var.wait()时传入了一个std::unique_lock对象,该对象在调用wait()时会自动释放互斥量,并在条件满足后重新获取,从而避免了死锁。同时,我们传递给wait()的Lambda表达式作为条件检查函数,确保只有在队列非空或接收到停止信号时线程才会被唤醒。

总结来说,互斥量和条件变量是并发编程中非常重要的同步工具。互斥量用于保护共享资源,确保同一时间只有一个线程能够访问它;而条件变量则用于协调线程间的执行顺序,允许线程等待某个条件成立。通过使用这些同步机制,我们可以编写出安全、可靠的并发代码。

相关文章
|
1月前
|
Linux API C++
c++多线程——互斥锁
c++多线程——互斥锁
|
11天前
|
API
linux---线程互斥锁总结及代码实现
linux---线程互斥锁总结及代码实现
|
1月前
|
安全 算法 Linux
【Linux 系统】多线程(线程控制、线程互斥与同步、互斥量与条件变量)-- 详解(下)
【Linux 系统】多线程(线程控制、线程互斥与同步、互斥量与条件变量)-- 详解(下)
|
1月前
|
存储 Linux 程序员
【Linux 系统】多线程(线程控制、线程互斥与同步、互斥量与条件变量)-- 详解(中)
【Linux 系统】多线程(线程控制、线程互斥与同步、互斥量与条件变量)-- 详解(中)
|
1月前
|
缓存 Linux 调度
【Linux 系统】多线程(线程控制、线程互斥与同步、互斥量与条件变量)-- 详解(上)
【Linux 系统】多线程(线程控制、线程互斥与同步、互斥量与条件变量)-- 详解(上)
|
1月前
|
安全 Linux 调度
【linux线程(二)】线程互斥与线程同步
【linux线程(二)】线程互斥与线程同步
|
1月前
|
存储 安全 Java
【探索Linux】P.21(多线程 | 线程同步 | 条件变量 | 线程安全)
【探索Linux】P.21(多线程 | 线程同步 | 条件变量 | 线程安全)
25 0
|
1月前
|
算法 安全 Linux
【探索Linux】P.20(多线程 | 线程互斥 | 互斥锁 | 死锁 | 资源饥饿)
【探索Linux】P.20(多线程 | 线程互斥 | 互斥锁 | 死锁 | 资源饥饿)
22 0
|
1月前
|
安全
并发编程之变量的线程安全分析的详细解析
并发编程之变量的线程安全分析的详细解析
17 0
|
1天前
|
数据采集 Java Unix
10-多线程、多进程和线程池编程(2)
10-多线程、多进程和线程池编程

热门文章

最新文章