美文网首页
同步并发操作

同步并发操作

作者: wenmingxing | 来源:发表于2018-04-24 17:18 被阅读11次

    本文主要说明条件变量的一些知识。

    I、等待一个时间或其他条件

    使用C++标准库提供的工具去等待事件的发生。通过另一个线程触发等待事件的机制是最基本的唤醒方式,这种机制就是条件变量

    1.1 等待条件达成

    C++标准库对条件变量有两套实现:std::condition_variablestd::condition_variable_any。两者都是需要与一个互斥量才能工作(互斥量是为了同步)。

    下面代码说明了当有数据需要处理时,唤醒休眠中的线程对其进行处理的方式:

    #include<iostream>
    #include<mutex>
    #include<condition_variable>
    #include<queue>
    #include<thread>
    
    using namespace std;
    
    std::mutex mut;
    std::queue<data_chunk> data_queue;  //数据队列
    std::condition_variable data_cond;
    
    void data_preparation_thread() {
        while (more_data_to_prepare()) {
            data_chunk const data = prepare_data();
            std::lock_guard<std::mutex> lk(mut);    //对队列上锁,然后添加数据
            data_queue.push(data);  //
            data_cond.notify_one(); //通知等待线程
        }
    }
    
    //data处理线程
    void data_processing_thread() {
        while (true) {
            std::unique_lock<std::mutex> lk(mut);   //对互斥量上锁,保护条件变量
    
            /* wait传入一个互斥量和检查条件
             * wait检查这些条件,如果条件不满足(为false)
             * wait将解锁互斥量,并将这个线程置于阻塞或等待状态。
             * 当准备数据的线程调用notify通知条件变量时,处理数据的线程从休眠中苏醒,重新获取互斥锁。
             * 并对条件再次检查,在条件满足的情况下,从wait返回并继续持有锁。当条件不满足时,线程对互斥量解锁,并重新开始等待。
    
             * 如果互斥量在线程休眠期间保持锁住状态,准备数据的线程将无法锁住互斥量,也就无法添加新的数据到队列中。
    
            */
            data_cond.wait(lk, []{return !data_queue.empty();});    //等待条件
            data_chunk data = data.front();
            data_queue.pop();
            lk.unlock();    //
            process(data);
    
            if (is_last_chunk(data))
                break;
        }
    }
    
    

    II、使用期望等待一次性事件

    C++标准库模型将一次性事件成为期望(future)。当一个线程需要等待一个特定的一次性事件时,在某种程度上来说它就需要知道这个事件在未来的表现形式。之后,这个下线程会周期性的等待或检查事件是否触发;在检查期间也会执行其他任务。另外,在等待任务期间它可以先执行另外一些任务,知道对应的任务触发,而后等待期望的状态变为就绪(ready)。当时间发生时,这个期望就不会被重置。

    C++标准库中,使用两种模板实现了两种期望:唯一期望std::future<>和共享期望std::shared_future<>

    期望对象本身并不提供同步访问。当多个线程需要访问同一个独立期望对象时,它们必须使用互斥量或类似的同步机制对访问进行保护。

    2.1 带返回值的后台任务

    假设有一个需要长时间计算的任务,但是现在并不迫切需要这个计算结果。可以启动一个新线程来执行这个计算,但是这就意味着必须关注如何传回计算的结果,因为std::thread并不提供直接接收返回值的机制。这里需要std::async函数模板。当任务的结果并不着急要时,可以使用std::async启动一个异步任务。与std::thread对象等待方式不同,std::async会返回一个std::future对象,这个对象就是最终计算出来的结果。当需要这个值时,只需要调用这个对象的get()成员函数:

    #include<future>
    #include<iostream>
    
    using namespace std;
    
    int find_the_answer_to_ltuar();
    void do_other_stuff();
    
    int main() {
    
        //也可以想async中传递参数
        std::future<int> the_answer = std::async(find_the_answer_to_ltuae);
        do_other_stuff();
        cout << the_answer.get() << endl;
    
        return 0;
    }
    
    

    【参考】
    [1] 《C++ Concurrency In Action》

    相关文章

      网友评论

          本文标题:同步并发操作

          本文链接:https://www.haomeiwen.com/subject/lwjjlftx.html