【C++新特性】C++11新特性之多线程条件变量

213 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情

std::condition_variable相关

条件变量是c++11引入的一种同步机制,它可以阻塞一个线程或者个线程,直到有线程通知或者超时才会唤醒正在阻塞的线程,条件变量需要和锁配合使用,这里的锁就是上面介绍的std::unique_lock。

这里使用条件变量实现一个CountDownLatch:

class CountDownLatch {
    public:
    explicit CountDownLatch(uint32_t count) : count_(count);
​
    void CountDown() {
        std::unique_lock<std::mutex> lock(mutex_);
        --count_;
        if (count_ == 0) {
            cv_.notify_all();
        }
    }
​
    void Await(uint32_t time_ms = 0) {
        std::unique_lock<std::mutex> lock(mutex_);
        while (count_ > 0) {
            if (time_ms > 0) {
                cv_.wait_for(lock, std::chrono::milliseconds(time_ms));
            } else {
                cv_.wait(lock);
            }
        }
    }
​
    uint32_t GetCount() const {
        std::unique_lock<std::mutex> lock(mutex_);
        return count_;
    }
​
    private:
    std::condition_variable cv_;
    mutable std::mutex mutex_;
    uint32_t count_ = 0;
};

关于条件变量其实还涉及到通知丢失和虚假唤醒问题,因为不是本文的主题,这里暂不介绍,大家有需要可以留言。

std::future相关

c++11关于异步操作提供了future相关的类,主要有std::future、std::promise和std::packaged_task,std::future比std::thread高级些,std::future作为异步结果的传输通道,通过get()可以很方便的获取线程函数的返回值,std::promise用来包装一个值,将数据和future绑定起来,而std::packaged_task则用来包装一个调用对象,将函数和future绑定起来,方便异步调用。而std::future是不可以复制的,如果需要复制放到容器中可以使用std::shared_future。

std::promise与std::future配合使用

#include <functional>
#include <future>
#include <iostream>
#include <thread>
​
using namespace std;
​
void func(std::future<int>& fut) {
    int x = fut.get();
    cout << "value: " << x << endl;
}
​
int main() {
    std::promise<int> prom;
    std::future<int> fut = prom.get_future();
    std::thread t(func, std::ref(fut));
    prom.set_value(144);
    t.join();
    return 0;
}

std::packaged_task与std::future配合使用

#include <functional>
#include <future>
#include <iostream>
#include <thread>
​
using namespace std;
​
int func(int in) {
    return in + 1;
}
​
int main() {
    std::packaged_task<int(int)> task(func);
    std::future<int> fut = task.get_future();
    std::thread(std::move(task), 5).detach();
    cout << "result " << fut.get() << endl;
    return 0;
}

三者之间的关系

std::future用于访问异步操作的结果,而std::promise和std::packaged_task在future高一层,它们内部都有一个future,promise包装的是一个值,packaged_task包装的是一个函数,当需要获取线程中的某个值,可以使用std::promise,当需要获取线程函数返回值,可以使用std::packaged_task。