《C++并发编程实战》 第4章 并发操作的同步

C++并发中future与同步操作详解

4.2 使用future等待一次性事件发生

  • 两种future:独占future(unique future,即std::future<>)和共享future(shared future,即std::shared_future<>
#include <future>
#include <iostream>
int find_the_answer_to_ltuae();
void do_other_stuff();
int main()
{
    std::future<int> the_answer=std::async(find_the_answer_to_ltuae);
    do_other_stuff();
    std::cout<<"The answer is "<<the_answer.get()<<std::endl;
}
  • 不过,我们还能够给std::async()补充一个参数,以指定采用哪种运行方式。参数的类型是std::launch,其值可以是std::launch::deferred或std::launch::async[8]。前者指定在当前线程上延后调用任务函数,等到在future上调用了wait()或get(),任务函数才会执行;后者指定必须另外开启专属的线程,在其上运行任务函数。该参数的值还可以是std::launch::deferred | std::launch:: async,表示由std::async()的实现自行选择运行方式。最后这项是参数的默认值。若延后调用任务函数,则任务函数有可能永远不会运行

4.2.2 关联future实例和任务

三种方式其实代表了 C++ 并发编程中,从 “全自动” 到 “全手动” 的三种不同粒度的控制权。它们的核心目的都是为了获取一个 std::future(未来的结果),但**“谁来执行”以及“如何传递结果”**的方式不同。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.2.4 将异常保存到future中

  • 包装的任务函数在执行时抛出异常,则会代替本应求得的结果,被保存到future内并使其准备就绪。只要调用get(),该异常就会被再次抛出。

在这里插入图片描述

在这里插入图片描述

#include <iostream>
#include <future>
#include <thread>
#include <exception>

void bad_chef(std::promise<int> prom) {
    // 厨师拿到订单(promise)
    std::cout << "厨师: 我拿到订单了,但我不想做饭..." << std::endl;
    
    // 正常流程应该调用 prom.set_value(100);
    // 但是!这里什么都没做,函数结束了。
    // prom 是局部变量,函数结束时它会被【销毁/析构】。
} 

int main() {
    // 1. 创建承诺
    std::promise<int> prom;
    
    // 2. 获取未来的票据
    std::future<int> f = prom.get_future();

    // 3. 开启线程,把 promise 移交给厨师(移动语义)
    std::thread t(bad_chef, std::move(prom));

    // 4. 顾客等待取餐
    try {
        std::cout << "顾客: 我在等饭..." << std::endl;
        int result = f.get(); // 这里会阻塞等待
        std::cout << "顾客: 拿到饭了: " << result << std::endl;
    } 
    catch (const std::future_error& e) {
        // 5. 捕获到了 Broken Promise 异常!
        std::cout << "\n>>> 顾客生气了: 发生错误! <<<" << std::endl;
        std::cout << "    错误代码: " << e.code() << " (broken_promise)" << std::endl;
        std::cout << "    原因: 厨师(Promise)销毁前没有给值。" << std::endl;
    }

    t.join();
    return 0;
}

在这里插入图片描述

4.2.5 多个线程一起等待

C++ 并发编程中 “单次消费”与“多次广播”

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

4.3 限时等待

两种超时(timeout)机制可供选用:一是迟延超时(duration-based timeout)​,线程根据指定的时长而继续等待(如30毫秒)​;二是绝对超时(absolute timeout)​,在某特定时间点(time point)来临之前,线程一直等待。大部分等待函数都具有变体,专门处理这两种机制的超时。处理迟延超时的函数变体以“_for”为后缀,而处理绝对超时的函数变体以“_until”为后缀。

4.3.1 时钟类

在这里插入图片描述
在这里插入图片描述

4.3.2 时长类

在这里插入图片描述
在这里插入图片描述

  • 类型转换
    大单位 -> 小单位(自动/隐式): 1 小时 -> 3600 秒。这是绝对安全的,数据不会丢,所以可以自动转。
    在这里插入图片描述
    在这里插入图片描述

4.3.3 时间点类

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.4 运用同步操作简化代码

4.4.1 利用future进行函数式编程

list中的splice

它的核心作用是:将元素从一个 list 移动到另一个 list(或同一个 list 的不同位置)。核心优势:零拷贝,极速 (O(1)O(1)O(1))与 std::vector 不同,list 的 splice 操作不会复制或移动数据本身,也不会分配或释放内存。它只是简单地修改了节点之间的指针指向。
在这里插入图片描述

std::partition

它的核心作用是:根据你指定的条件(谓词),将容器中的元素“一分为二
在这里插入图片描述

#include <iostream>
#include <list>
#include <algorithm>
#include <thread>
#include <future>

template <typename T>
std::list<T> parallel_quick_sort(std::list<T> input)
{
    if (input.empty())
    {
        return input;
    }

    std::list<T> result;
    // 取出第一个元素作为基准
    result.splice(result.begin(), input, input.begin());
    T const &pivot = *result.begin();

    // 根据基准划分元素为两半
    auto divide_point = std::partition(input.begin(), input.end(), [&](T const &t)
                                       { return t < pivot; });
    // 将input中小于基准的元素取出放到lower_part中
    sdt::list<T> lower_part;
    lower_part.splice(lower_part.end(), input, input.begin(), divide_point);
    // 开启一个现场基础递归快排小于基准的部分
    std::future<std::list<T>> new_lower(std::async(&*parallel_quick_sort<T>, std::move(lower_part)));
    // 递归快排大于等于基准的部分
    auto new_higher(parallel_quick_sort(std::move(input)));
    // 将排好序的小于基准的部分加入结果中的基准的后面
    result.splice(result.end(), new_higher);
    // 等待小于基准的部分排好序后加入结果中的基准的前面
    result.splice(result.begin(), new_lower.get());
    // 这里的get()会阻塞直到new_lower完成
    return result;
}

在这里插入图片描述

四个概念纠清

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.4.2 使用消息传递进行同步

std::experimental

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值