C++11 condition_variable条件变量怎么使用

发布时间:2022-07-12 10:11:32 作者:iii
来源:亿速云 阅读:204

C++11 condition_variable条件变量怎么使用

目录

  1. 引言
  2. 条件变量概述
  3. 条件变量的基本用法
  4. 条件变量的典型应用场景
  5. 条件变量的注意事项
  6. 条件变量的高级用法
  7. 总结

引言

在多线程编程中,线程之间的同步是一个非常重要的问题。C++11引入了std::condition_variable,它是一种用于线程间同步的工具,允许一个或多个线程等待某个条件成立,或者通知其他线程条件已经成立。本文将详细介绍std::condition_variable的使用方法、典型应用场景以及注意事项。

条件变量概述

std::condition_variable是C++11标准库中提供的一种同步原语,用于在多线程环境中实现线程间的条件等待和通知。它通常与std::mutex一起使用,以确保线程在等待条件时能够正确地释放锁,并在条件满足时重新获取锁。

条件变量的核心思想是:一个线程可以等待某个条件成立,而另一个线程可以在条件成立时通知等待的线程。这种机制非常适合用于实现生产者-消费者模型、线程池等并发模式。

条件变量的基本用法

3.1 创建条件变量

要使用std::condition_variable,首先需要包含头文件<condition_variable>,然后创建一个std::condition_variable对象:

#include <condition_variable>

std::condition_variable cv;

3.2 等待条件

线程可以使用std::condition_variable::wait函数来等待某个条件成立。wait函数需要一个std::unique_lock<std::mutex>对象作为参数,并且在等待时会自动释放锁,直到条件满足时重新获取锁。

std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return condition; });

在上面的代码中,cv.wait会一直等待,直到conditiontruewait函数会自动释放lock,并在条件满足时重新获取锁。

3.3 通知条件

当一个线程满足某个条件时,可以使用std::condition_variable::notify_onestd::condition_variable::notify_all来通知等待的线程。

{
    std::lock_guard<std::mutex> lock(mtx);
    condition = true;
}
cv.notify_one();  // 或者 cv.notify_all();

在上面的代码中,notify_one会唤醒一个等待的线程,而notify_all会唤醒所有等待的线程。

条件变量的典型应用场景

4.1 生产者-消费者模型

生产者-消费者模型是多线程编程中的一个经典问题。生产者线程生成数据并将其放入缓冲区,而消费者线程从缓冲区中取出数据并进行处理。条件变量可以很好地解决生产者和消费者之间的同步问题。

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

std::queue<int> buffer;
std::mutex mtx;
std::condition_variable cv;
const 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() {
    while (true) {
        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;
}

在上面的代码中,生产者线程在缓冲区未满时生成数据并放入缓冲区,消费者线程在缓冲区不为空时从缓冲区中取出数据。条件变量cv用于同步生产者和消费者线程。

4.2 线程池

线程池是一种常见的并发模式,它通过预先创建一组线程来处理任务。条件变量可以用于线程池中的任务调度。

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

class ThreadPool {
public:
    ThreadPool(size_t num_threads) {
        for (size_t i = 0; i < num_threads; ++i) {
            workers.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex);
                        this->condition.wait(lock, [this]{ return this->stop || !this->tasks.empty(); });
                        if (this->stop && this->tasks.empty()) {
                            return;
                        }
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }
                    task();
                }
            });
        }
    }

    template<class F, class... Args>
    void enqueue(F&& f, Args&&... args) {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            tasks.emplace([=]{ return f(args...); });
        }
        condition.notify_one();
    }

    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            stop = true;
        }
        condition.notify_all();
        for (std::thread &worker : workers) {
            worker.join();
        }
    }

private:
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop = false;
};

int main() {
    ThreadPool pool(4);
    for (int i = 0; i < 8; ++i) {
        pool.enqueue([i] {
            std::cout << "Task " << i << " is running" << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));
            std::cout << "Task " << i << " is done" << std::endl;
        });
    }
    return 0;
}

在上面的代码中,线程池中的线程会等待任务队列中的任务,并在任务到来时执行任务。条件变量condition用于通知线程有新任务到来。

条件变量的注意事项

5.1 虚假唤醒

虚假唤醒是指线程在没有收到通知的情况下从wait函数中返回。为了防止虚假唤醒,通常需要在wait函数中使用一个谓词来检查条件是否真正满足。

cv.wait(lock, []{ return condition; });

在上面的代码中,wait函数会在条件满足时返回,从而避免虚假唤醒。

5.2 死锁

在使用条件变量时,如果锁的使用不当,可能会导致死锁。例如,如果一个线程在持有锁的情况下调用wait,而另一个线程在持有锁的情况下调用notify,可能会导致死锁。

为了避免死锁,通常需要在调用wait之前释放锁,并在wait返回时重新获取锁。

5.3 条件变量的性能

条件变量的性能通常比忙等待(busy-waiting)要好,因为它可以让线程在等待时进入睡眠状态,从而减少CPU的占用。然而,条件变量的性能也受到锁的竞争和上下文切换的影响,因此在设计多线程程序时需要仔细考虑锁的粒度和条件变量的使用。

条件变量的高级用法

6.1 超时等待

std::condition_variable提供了wait_forwait_until函数,允许线程在等待条件时设置超时时间。

std::cv_status status = cv.wait_for(lock, std::chrono::seconds(1));
if (status == std::cv_status::timeout) {
    // 超时处理
}

在上面的代码中,wait_for函数会在1秒后返回,如果条件仍未满足,则返回std::cv_status::timeout

6.2 条件变量与锁的结合

条件变量通常与std::mutexstd::unique_lock结合使用,以确保线程在等待条件时能够正确地释放锁,并在条件满足时重新获取锁。

std::mutex mtx;
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return condition; });

在上面的代码中,wait函数会自动释放lock,并在条件满足时重新获取锁。

总结

std::condition_variable是C++11中用于线程间同步的重要工具,它允许线程等待某个条件成立,并在条件满足时通知其他线程。通过合理地使用条件变量,可以实现复杂的多线程同步机制,如生产者-消费者模型、线程池等。然而,在使用条件变量时也需要注意虚假唤醒、死锁等问题,以确保程序的正确性和性能。

希望本文能够帮助你更好地理解和使用C++11中的std::condition_variable

推荐阅读:
  1. python 线程条件变量Condition(31)
  2. python中condition条件变量的使用方法

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

c++

上一篇:redis主从哨兵模式怎么实现一主二从

下一篇:ahooks控制时机的hook如何实现

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》