More than code

More Than Code
The efficiency of your iteration of reading, practicing and thinking decides your understanding of the world.
  1. 首页
  2. daily
  3. 正文

Daily C/C++ condition_variable

2021年10月7日 561点热度 0人点赞 0条评论

Daily C/C++ condition_variable

之前有说要出一个条件变量的讲解

说到头我还是感觉cppreference讲的好,这里就总结一下

参考文章

条件变量也是一种用于同步的对象,他能够阻塞多个线程,直到另一个线程修改了共享变量并通知条件变量

这是啥意思呢,我们先来一个例子

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

std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;

void worker_thread()
{
    // 等待直至 main() 发送数据
    std::unique_lock<std::mutex> lk(m);
    cv.wait(lk, []{return ready;});

    // 等待后,我们占有锁。
    std::cout << "Worker thread is processing data\n";
    data += " after processing";

    // 发送数据回 main()
    processed = true;
    std::cout << "Worker thread signals data processing completed\n";

    // 通知前完成手动解锁,以避免等待线程才被唤醒就阻塞(细节见 notify_one )
    lk.unlock();
    cv.notify_one();
}

int main()
{
    std::thread worker(worker_thread);

    data = "Example data";
    // 发送数据到 worker 线程
    {
        std::lock_guard<std::mutex> lk(m);
        ready = true;
        std::cout << "main() signals data ready for processing\n";
    }
    cv.notify_one();

    // 等候 worker
    {
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return processed;});
    }
    std::cout << "Back in main(), data = " << data << '\n';

    worker.join();
}

首先我们会创建一个worker线程,然后持有锁。

接着我们会使用cv.wait()进入等待状态,这时候worker线程就会释放掉这个锁,同时等待谓词返回真,这里就是等待ready为真

然后来到main中,他会首先获得锁,然后修改ready,并之后通知条件变量唤醒线程

接着worker被唤醒以后,首先他会尝试去获得锁,然后检查谓词,如果满足,他就会退出等待,并执行下一步的代码

与此同时,main中也会尝试去获得锁,并等待worker修改processed

worker中被唤醒后就会一直持有锁,他会修改processed,然后再是释放掉锁,最后通知条件变量去唤醒主线程

看完这个例子,再看一下条件变量的使用方法就会清楚很多

有意要修改共享变量的线程必须要获得锁(因为是共享变量)

然后再修改共享变量,最后执行notify_one或者notify_all来唤醒其他等待的线程。

即使共享变量是原子的,我们也必须要在互斥的状态下修改他,以保证共享变量被正确的发布(个人猜测可能和高速缓存一致性有关,因为锁可以帮我们来统一高速缓存)

有意在条件变量上等待的线程必须获得std::unique_lock<std::mutex>,因为我们是在通过这个锁进行同步

然后我们或者会去检查条件,或者执行等待。

当条件变量被通知时,线程将被唤醒,同时他会尝试去获得互斥(因为要先有锁才能去读共享变量),然后去检查条件

具体的细节上的使用方法大家可以参阅cppreference

这里就说两个,notify和wait,也是最主要的两个

notify就是在条件变量上解阻塞一个或若干个线程,让他们进行检查

wait则会原子的释放lock,并阻塞当前的线程。我们可以通过传入一个谓词来让被唤醒的线程检查条件

等价于

while (!pred()) {
    wait(lock);
}

注意,通知线程的时候不必保有互斥锁。实际上这样做是劣化,因为被通知的线程将立即再次阻塞,等待通知线程释放锁。

有些实现会辨识此情形,在通知调用中,他会从条件变量队列转移等待线程到互斥队列,而不唤醒他。

标签: c++
最后更新:2021年10月7日

sheep

think again

点赞
< 上一篇
下一篇 >

文章评论

取消回复

COPYRIGHT © 2021 heavensheep.xyz. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS