Jupyter AI

17 互斥量与条件变量

📅发表日期: 2024-08-10

🏷️分类: Cplus进阶

👁️阅读次数: 0

在上一篇中,我们讨论了多线程编程的基本概念,包括线程的创建、同步以及一些简单的线程调度。在这一篇中,我们将深入探讨多线程编程中至关重要的两种同步机制:互斥量(Mutex)和条件变量(Condition Variable)。这两者是确保多个线程安全高效地共享资源的关键工具。

互斥量(Mutex)

互斥量是一种用于保护共享资源的同步机制。它确保在任意时刻只有一个线程可以访问某个资源,从而避免了并发访问时出现的数据竞争或不一致性问题。

1. 创建和使用互斥量

在 C++ 中,我们可以使用 std::mutex 类来创建互斥量。以下是一个基本示例,展示了如何创建一个互斥量并在多个线程之间安全地访问共享变量。

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

std::mutex mtx; // 创建互斥量
int shared_data = 0;

void increment() {
    for (int i = 0; i < 1000; ++i) {
        mtx.lock(); // 上锁
        ++shared_data; // 访问共享数据
        mtx.unlock(); // 解锁
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();

    std::cout << "Final shared data: " << shared_data << std::endl;
    return 0;
}

在这个例子中,两个线程 t1t2 都在增量 shared_data。使用 mtx.lock()mtx.unlock() 来确保在对 shared_data 进行操作时,不会有其他线程同时访问。

2. RAII 和 std::lock_guard

直接使用 lock()unlock() 是容易出错的,如果在上锁后发生异常,那么互斥量将不会被解锁。为了避免这种情况,C++11 提供了 std::lock_guard 类,它可以帮助我们确保在作用域结束时自动释放互斥量:

void increment() {
    for (int i = 0; i < 1000; ++i) {
        std::lock_guard<std::mutex> lock(mtx); // RAII 风格上锁
        ++shared_data;
    }
}

这样,无论函数如何退出,互斥量都会被安全地解锁。

条件变量(Condition Variable)

条件变量是一种同步机制,用于通知一个或多个线程某个条件已经发生。它通常与互斥量结合使用,以实现线程间的等待和通知。

1. 创建和使用条件变量

条件变量的基本使用建构在 std::condition_variable 上。以下是一个生产者-消费者的例子,使用条件变量来协调生产者和消费者的工作。

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

std::mutex mtx;
std::condition_variable cv;
std::queue<int> buffer;
const unsigned int max_buffer_size = 10;

void producer() {
    for (int i = 0; i < 20; ++i) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [] { return buffer.size() < max_buffer_size; }); // 等待条件

        buffer.push(i);
        std::cout << "Produced: " << i << std::endl;

        lock.unlock();
        cv.notify_all(); // 通知消费者
    }
}

void consumer() {
    for (int i = 0; i < 20; ++i) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [] { return !buffer.empty(); }); // 等待条件

        int value = buffer.front();
        buffer.pop();
        std::cout << "Consumed: " << value << std::endl;

        lock.unlock();
        cv.notify_all(); // 通知生产者
    }
}

int main() {
    std::thread prod(producer);
    std::thread cons(consumer);

    prod.join();
    cons.join();

    return 0;
}

在这个例子中,生产者线程在 buffer 中放入数字,而消费者线程则从 buffer 中取出数字。cv.wait(lock, condition) 用来使线程在条件不满足时挂起,并在条件满足时恢复执行。通过 cv.notify_all(),生产者和消费者可以相互通知以继续执行。

总结

在多线程编程中,互斥量条件变量是确保数据一致性和正确性的重要工具。互斥量帮助我们保护共享数据的访问,而条件变量则允许线程之间进行有效的通信。在实际编程中,合理使用这些工具可以使得我们的程序更安全、更高效。

在下一篇中,我们将继续探讨如何设计线程安全的数据结构,并介绍如何在多线程环境中安全地使用这些数据结构。

💬 评论

暂无评论