随笔 - 741  文章 - 0  评论 - 260  阅读 - 416万

std::condition_variable

std::condition_variable 是 C++11 标准库中提供的一个同步原语,用于线程间的条件等待和通知机制。它允许一个或多个线程等待某个条件变为真(即被满足),并通过另一个线程的通知来唤醒这些等待的线程。

主要特点

  • 条件等待:线程可以等待某个条件变量,直到其他线程通知该条件已被满足。
  • 通知机制:支持单个通知 (notify_one) 和全部通知 (notify_all)。
  • 与互斥锁结合使用:std::condition_variable 需要与 std::mutex 结合使用,以确保线程安全。

成员函数

以下是 std::condition_variable 的主要成员函数:

  • 等待函数:

    • void wait(std::unique_lock<std::mutex>& lock);:阻塞当前线程,直到条件变量被通知。
    • template<typename Predicate> void wait(std::unique_lock<std::mutex>& lock, Predicate pred);:阻塞当前线程,直到条件变量被通知且谓词返回 true
  • 通知函数:

  • void notify_one();:唤醒一个等待该条件变量的线程(如果有)。

   void notify_all();:唤醒所有等待该条件变量的线程。

使用步骤

  1. 创建互斥锁:通常使用 std::mutex 或其派生类。
  2. 创建条件变量:使用 std::condition_variable
  3. 获取互斥锁:在进入等待状态之前,必须先锁定互斥锁。
  4. 等待条件变量:调用 wait 方法等待条件变量被通知。
  5. 修改共享数据:在另一个线程中修改共享数据,并在修改后通知条件变量。
  6. 通知等待的线程:调用 notify_one 或 notify_all 方法通知等待的线程。

使用示例

示例 1:生产者-消费者问题

这是一个经典的生产者-消费者问题,展示了如何使用 std::condition_variable 来同步生产者和消费者线程

 

复制代码
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>

std::queue<int> queue;
std::mutex mutex;
std::condition_variable cond_var;
const int max_queue_size = 10;

void producer(int id) {
    for (int i = 0; i < 20; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));  // 模拟生产时间

        std::unique_lock<std::mutex> lock(mutex);
        cond_var.wait(lock, [] { return queue.size() < max_queue_size; });  // 等待队列不满

        queue.push(i);
        std::cout << "Producer " << id << " produced: " << i << std::endl;

        lock.unlock();
        cond_var.notify_one();  // 通知消费者
    }
}

void consumer(int id) {
    while (true) {
        std::unique_lock<std::mutex> lock(mutex);
        cond_var.wait(lock, [] { return !queue.empty(); });  // 等待队列不为空

        if (!queue.empty()) {
            int value = queue.front();
            queue.pop();
            std::cout << "Consumer " << id << " consumed: " << value << std::endl;

            if (value == 19) {
                break;  // 当最后一个元素被消费时退出循环
            }
        }

        lock.unlock();
        cond_var.notify_one();  // 通知生产者
    }
}

int main() {
    const int num_producers = 2;
    const int num_consumers = 2;

    std::vector<std::thread> producers;
    std::vector<std::thread> consumers;

    for (int i = 0; i < num_producers; ++i) {
        producers.emplace_back(producer, i);
    }

    for (int i = 0; i < num_consumers; ++i) {
        consumers.emplace_back(consumer, i);
    }

    for (auto& p : producers) {
        p.join();
    }

    for (auto& c : consumers) {
        c.join();
    }

    return 0;
}
复制代码

 

解释
  • 生产者:每个生产者线程尝试将一个整数添加到队列中。如果队列已满,则等待条件变量 cond_var 被通知(即队列有空位)。
  • 消费者:每个消费者线程从队列中取出一个整数。如果队列为空,则等待条件变量 cond_var 被通知(即队列中有数据)。
  • 互斥锁:使用 std::mutex 来保护对共享队列的操作,确保线程安全。
  • 条件变量:使用 std::condition_variable 来协调生产者和消费者之间的同步。

示例 2:简单的任务调度器

以下是一个简单的任务调度器的例子,展示如何使用 std::condition_variable 来管理任务队列。

 

复制代码
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <vector>

std::queue<std::function<void()>> task_queue;
std::mutex mutex;
std::condition_variable cond_var;
bool done = false;

void worker(int id) {
    while (true) {
        std::function<void()> task;

        {
            std::unique_lock<std::mutex> lock(mutex);
            cond_var.wait(lock, [] { return !task_queue.empty() || done; });

            if (done && task_queue.empty()) {
                break;
            }

            task = task_queue.front();
            task_queue.pop();
        }

        task();  // 执行任务
        std::cout << "Worker " << id << " executed a task." << std::endl;
    }
}

void submit_task(const std::function<void()>& task) {
    {
        std::lock_guard<std::mutex> lock(mutex);
        task_queue.push(task);
    }
    cond_var.notify_one();  // 通知工作线程
}

int main() {
    const int num_workers = 3;

    std::vector<std::thread> workers;
    for (int i = 0; i < num_workers; ++i) {
        workers.emplace_back(worker, i);
    }

    // 提交一些任务
    for (int i = 0; i < 10; ++i) {
        submit_task([i] {
            std::cout << "Task " << i << " is being processed." << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        });
    }

    // 等待所有任务完成
    std::this_thread::sleep_for(std::chrono::seconds(2));

    // 设置结束标志并通知所有工作线程
    {
        std::lock_guard<std::mutex> lock(mutex);
        done = true;
    }
    cond_var.notify_all();

    for (auto& w : workers) {
        w.join();
    }

    return 0;
}
复制代码

 

解释
  • 任务队列:task_queue 存储需要执行的任务。
  • 工作线程:每个工作线程从任务队列中取出一个任务并执行。如果没有任务可执行,则等待条件变量 cond_var 被通知。
  • 提交任务:submit_task 函数将新任务添加到任务队列中,并通知一个等待的工作线程。
  • 结束标志:done 标志用于指示所有任务是否已完成,当设置为 true 并通知所有工作线程时,它们会检查队列是否为空并退出循环。

注意事项

  1. 死锁:确保在调用 wait 方法之前已经持有互斥锁,并且在条件满足后正确解锁。否则可能导致死锁。

  2. 虚假唤醒:wait 方法可能会由于虚假唤醒而返回,即使没有实际的通知。因此,通常需要结合谓词(predicate)来检查条件是否真正满足。

  3. 性能考虑:频繁的锁争用会影响性能。尽量减少锁的持有时间和频率,以提高并发性能。

  4. 内存顺序:std::condition_variable 内部已经处理了必要的内存顺序问题,因此用户不需要额外关注这一点。

 

总结

 

  • std::condition_variable 提供了一种高效的线程间同步机制,适用于需要等待某些条件满足的场景。
  • 它通常与 std::mutex 结合使用,以确保线程安全。
  • 通过 waitnotify_one 和 notify_all 方法,可以实现灵活的线程同步逻辑。

 

如果你有更多具体的需求或问题,请告诉我!我会尽力提供帮助。

 

posted on   莫水千流  阅读(372)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2018-01-10 研究|数据预处理|归一化 (标准化)
2017-01-10 Git Submodule管理项目子模块
2017-01-10 git submodule的使用
2017-01-10 More C++ Idioms
2017-01-10 C++11
2017-01-10 C++11常用特性的使用经验总结(转载)
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示