互斥锁 条件变量

互斥锁 条件变量

在C++中,互斥锁(mutex)和条件变量(condition variable)是用于线程同步和通信的重要工具。互斥锁被用来保护共享数据结构,以确保在任意时刻只有一个线程能够访问该数据结构。而条件变量则用于在线程之间进行通信,允许一个线程等待某个特定的条件成立,然后被另一个线程唤醒。

互斥锁在C++中通过std::mutex类实现。它提供了lock()和unlock()方法,可以使用这些方法来保护共享数据。当一个线程调用lock()方法时,如果互斥锁已经被其他线程锁定,那么该线程将被阻塞,直到锁被释放为止。一旦线程获得了锁,它将执行关键代码段,并在退出临界区时使用unlock()方法来释放锁。这样可以确保在任意时刻只有一个线程在访问共享资源。

条件变量在C++中通过std::condition_variable类实现。条件变量用于线程之间的通信。一个线程可以通过等待条件变量,挂起自己,并释放已经持有的锁,等待其他线程满足某个特定的条件后发出唤醒信号。其他线程可以在满足条件时,通过notify_one()或notify_all()方法发送唤醒信号。唤醒的线程将重新尝试获得互斥锁,并继续执行。

以下是一个使用互斥锁和条件变量的示例:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void worker()
{
    
    
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, []() {
    
     return ready; });
    std::cout << "Worker thread is executing." << std::endl;
    // 执行相关操作
}

int main()
{
    
    
    std::thread t(worker);
    
    // 做一些任务
    
    {
    
    
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;
    }
    cv.notify_one();
    
    t.join();
    
    return 0;
}

主线程创建了一个工作线程,并在工作线程内部等待条件变量ready满足。主线程在一些任务后将ready设置为true并使用cv.notify_one()唤醒工作线程。工作线程被唤醒后,获得互斥锁,输出一条消息,并执行其余的操作。

这是一个简单的互斥锁和条件变量的示例,用于说明它们的基本用法。在实际的应用中,你可能需要更复杂的同步和通信方案,这时可以使用多个互斥锁、条件变量和其他同步原语来实现。

lock_guard 函数讲解

std::lock_guard是一个RAII(资源获取即初始化)类,它提供了一种自动获取互斥锁的机制,可以方便地保护临界区。

当使用std::lock_guard对象时,在对象的构造函数中获取互斥锁,在对象的析构函数中释放互斥锁。这样可以确保在任何情况下(包括异常抛出和提前返回),互斥锁都会被正确地释放,从而防止资源泄漏和死锁。

std::lock_guard典型的用法是在临界区的开始和结束之间创建一个std::lock_guard对象。当创建std::lock_guard对象时,需要传递一个互斥锁对象作为参数。例如:

std::mutex mtx;
// ...其他代码...

{
    
    
    std::lock_guard<std::mutex> lock(mtx);
    // 临界区代码
    // 互斥锁在这里被自动获取
    // 无论代码中是否有异常或提前返回,互斥锁都会在这里被释放
}
// ...其他代码...

在上述示例中,std::lock_guard对象被创建在一个作用域内,并命名为lock。在lock对象的作用域内,可以自由地访问临界区的共享资源。当lock对象超出其作用域时,它的析构函数被调用,互斥锁将被自动释放。

使用std::lock_guard相比手动调用lock()和unlock()方法,具有以下好处:

简洁性:使用std::lock_guard可以将获取和释放互斥锁的操作放在同一个作用域内,使代码逻辑更加清晰和简洁。
异常安全:std::lock_guard对象在构造时获取互斥锁,无论是否有异常,它的析构函数都会被调用来释放互斥锁,确保异常安全性。
防止忘记释放锁:由于锁的释放是自动的,不需要手动调用unlock()方法,因此避免了忘记释放锁而导致的死锁问题。
需要注意的是,std::lock_guard是一种独占锁的方式,它使用的是std::mutex(互斥锁)而不是std::shared_mutex(共享互斥锁)。如果需要在多个线程之间共享锁,需要使用std::shared_lock或std::unique_lock来代替std::lock_guard。

猜你喜欢

转载自blog.csdn.net/neuzhangno/article/details/131521027