详解C++11中的线程锁和条件变量
C++11中提供了一系列的线程同步机制,包括线程锁和条件变量。线程锁主要是为了保护共享资源,防止多个线程同时对同一块内存区域进行操作而发生冲突;而条件变量则主要是为了线程之间的协作,当一个线程等待某个条件成立时,可以通过条件变量来阻塞当前线程,直到条件被满足为止。
线程锁
Mutex
Mutex(互斥锁)是最基本的线程锁,它可以确保同一时刻只有一个线程能够访问被保护的共享资源,从而避免竞争条件的发生。C++11中提供了多种Mutex,包括std::mutex、std::recursive_mutex、std::timed_mutex等等。以std::mutex为例,下面是如何使用Mutex进行线程同步的示例代码:
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mutex;
void print(int i) {
mutex.lock();
std::cout << "Thread " << i << " is running" << std::endl;
mutex.unlock();
}
int main() {
std::thread threads[5];
for(int i = 0; i < 5; i++) {
threads[i] = std::thread(print, i);
}
for(std::thread& t : threads) {
t.join();
}
return 0;
}
上面的代码中,我们首先创建了一个std::mutex对象,然后在print函数中对其加锁,确保同一时刻只有一个线程可以输出信息,输出完成后再进行解锁操作。在主函数中创建了5个线程,并将它们执行print函数。最后使用join等待所有线程执行结束。需要注意的是,如果Mutex没有被正确释放,可能会导致死锁或者程序卡住。
RecursiveMutex
RecursiveMutex又称为可重入锁,是一种支持同一线程多次获取锁的锁类型。如果某个线程已经获取了RecursiveMutex并且没有释放,则可以再次获取该Mutex,同时需要确保每次获取和释放Mutex的次数要一样,不然会导致死锁。以下是使用RecursiveMutex的示例代码:
#include <iostream>
#include <thread>
#include <mutex>
int count = 0;
std::recursive_mutex mutex;
void increment(int i) {
mutex.lock();
std::cout << "thread " << i << " is incrementing count" << std::endl;
++count;
if(count < 10) {
increment(i);
}
mutex.unlock();
}
int main() {
std::thread threads[5];
for(int i = 0; i < 5; i++) {
threads[i] = std::thread(increment, i);
}
for(std::thread& t : threads) {
t.join();
}
std::cout << "final count = " << count << std::endl;
return 0;
}
上面的代码中,我们首先定义了一个std::recursive_mutex和一个计数器count。在increment函数中,每个线程会加锁并且对count进行加1操作,如果count小于10,则递归调用increment函数。需要注意的是,在每次递归调用时,都会再次获取Mutex,如果Mutex没有被正确释放,可能会导致死锁。
条件变量
条件变量是用于线程之间同步的重要机制,它可以实现线程之间的协作,使得某个线程等待某个特定条件满足后才继续执行。C++11中提供了std::condition_variable和std::condition_variable_any两种条件变量实现方式,前者只支持std::unique_lock并且需要和std::mutex一起使用,后者则可以与任何符合要求的锁类型一起使用。以下是使用std::condition_variable的示例代码:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mutex;
std::condition_variable cv;
bool ready = false;
void worker() {
std::unique_lock<std::mutex> lock(mutex);
while(!ready) {
cv.wait(lock);
}
std::cout << "worker thread is running" << std::endl;
}
void notifier() {
std::lock_guard<std::mutex> lock(mutex);
ready = true;
cv.notify_one();
}
int main() {
std::thread t1(worker);
std::thread t2(notifier);
t1.join();
t2.join();
return 0;
}
上面的代码中,我们首先定义了一个Mutex和一个条件变量cv,再定义了一个标记变量ready,用于标记是否满足某个条件。在worker函数中,我们首先对Mutex进行加锁,然后进入while循环等待条件变量满足,如果条件变量没有满足,则会调用cv.wait()函数阻塞当前线程,同时会释放Mutex,直到收到notify_one通知后,才会继续执行。而在notifier函数中,我们首先对Mutex进行lock_guard加锁,然后将ready标记为true,并调用cv.notify_one()函数通知所有正在等待的线程。
在实际使用中,我们可以使用条件变量来实现多个线程之间的同步协作,例如生产者消费者模型、优先级队列等。以下是使用条件变量实现一个简单的生产者消费者模型的示例代码:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>
std::queue<int> q;
std::mutex mutex;
std::condition_variable cv;
void producer() {
for(int i = 0; i < 10; i++) {
std::lock_guard<std::mutex> lock(mutex);
q.push(i);
std::cout << "produced " << i << std::endl;
cv.notify_one();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
void consumer() {
while(true) {
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [](){return !q.empty();});
int value = q.front();
q.pop();
std::cout << "consumed " << value << std::endl;
}
}
int main() {
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
return 0;
}
上面的代码中,我们定义了一个队列q、一个Mutex和一个条件变量cv,同时定义了一个producer函数和一个consumer函数。在producer函数中,我们依次向队列中添加元素,并且通过条件变量cv通知正在等待的线程可以进行消费了。在consumer函数中,我们使用while循环不断地从队列中获取元素,如果队列中没有元素,就会进入阻塞状态,直到通过cv.wait()函数收到生产者的通知后,才会继续执行。
需要注意的是,在使用条件变量的时候,我们通常需要使用std::unique_lock来对Mutex进行加锁解锁操作,同时在调用cv.wait()函数的时候,可以通过lambda表达式来判断条件是否满足。
总结
在本篇攻略中,我们详细介绍了C++11中的线程锁和条件变量机制,包括Mutex、RecursiveMutex、std::condition_variable和std::condition_variable_any,在讲解的过程中,也给出了对应的示例代码。线程同步是多线程编程中非常重要的一环,掌握好线程锁和条件变量的使用方法,对于编写高质量的并发程序有着重要的意义。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解C++11中的线程锁和条件变量 - Python技术站