• 忘掉天地
  • 仿佛也想不起自己
bingliaolongBingliaolong  2020-05-22 15:32 Aet 隐藏边栏 |   抢沙发  17 
文章评分 7 次,平均分 5.0

一次性事件

  1. C++标准库模型将这种一次性事件称为期望future
  2. 当一个线程需要等待一个特定的一次性事件时,在某种程度上来说它需要知道这个事件在未来的表现形式。之后,这个线程会周期性的等待或检查,事件是否出发
    1. 在检查期间也会执行其他任务,直到对应的任务触发,而后等待期望的状态会变为就绪ready

future

  1. 一个期望可能是数据相关的,也可能不是
  2. 当事件发生时,这个期望就不能被重置
  3. 在C++标准库中,有两种期望:
    1. 唯一期望:std::future<>,只能与一个指定事件相关联
    2. 共享期望:std::shared_future<>,能关联多个事件
  4. std::future的实例

async

  1. 在不需要立刻得到结果的时候,可以使用std::async来启动一个异步任务。
  2. std::async返回一个std::future对象,而不是返回一个线程对象,future对象最终将持有函数的返回值。
  3. 当我们需要这个值时,只要在future上调用get(),线程就会阻塞直到future就绪,然后返回该值。
    1. 换句话而言,如果任务正常进行中,调用the_answer.get()将会阻塞住。如果任务已经运行结束了,调用the_answer.get()将不会阻塞,并且将得到任务的结果

  1. 在默认情况下,期望是否进行等待取决于std::async是否启动一个线程,或是否有任务正在进行同步
  2. 在大多数情况上一条而言,但是,我们还可以在函数调用之前,向std::async传递一个std::launch类型的额外参数,用这个参数来表明函数调用被延迟到wait或get函数调用时才执行
    1. std::launch::async表明函数必须在其所在的独立线程上执行
    2. std::launch::deferred|std::launch::async表明实现可以选择这两种方式的一种,最后一个选项是默认的。当函数调用被延迟,它可能不会在运行了

  1. std::launch::async策略
    1. 当使用这个策略时,任务会在一个独立的线程中立即启动。这确保了任务会异步地运行
    2. 返回的std::future对象用于检索在新线程中执行的任务的结果
    3. 一般情况下,这意味着任务得到了真正的并发执行
  2. std::launch::deferred策略
    1. 使用此策略时,函数的执行会被延迟。
    2. 函数只有在你首次尝试获取std::future的结果时(例如,调用get()wait())才会执行
    3. 函数会在调用get()wait()的同一线程中执行,而不是一个新的线程
    4. 更像是一种“延迟计算”或“懒惰计算”策略,而不是真正的异步执行
  3. 组合上面两个策略
    1. 如果你调用std::async而不明确指定启动策略,它将默认为std::launch::async | std::launch::deferred
    2. 这意味着库可以选择其中的任何一个策略来执行。这可能根据库的实现和当前系统的状态来决定
  4. 组合介绍

packaged_taskfuture

  1. std::packaged_task<>将一个future绑定到一个函数或可调用对象上。
  2. std::packaged_task<>对象被调用时,它就调用相关联的函数或可调用对象,并且让future就绪,将返回值作为关联数据储存。
  3. std::package_task<>类模板的模板参数为函数签名。当我们构造一个实例的时候,必须传入一个函数或可调用对象,它可以接受指定的参数并返回指定的返回类型
  4. 指定函数签名的返回类型可以用来标识,从get_future()返回的std::future<>的类型,不过函数签名的参数列表,可以用来指定“打包任务”的函数调用操作符。

  • 线程循环收消息直到收到GUI停止的消息
  • 反复轮询处理GUI消息
  • 判断任务队列中的消息,没有消息再次循环,否则从队列中提取任务
  • 解除队列中的锁并允许任务
  • 在队列上利用提供的函数创建一个新的任务包,通过调用get_future成员函数从任务中获取future
  • 将任务置于列表之上
  1. 重复等待一次性事件
    1. 检查future是否准备好了结果,如果没有再去做其他事情,等其他事情处理完毕了,再重新检查
    2. 如果此时已经就绪了,就处理,如果还是没有继续,继续循环处理其他事情

std::promise

  1. std::promise提供设置值的方式,它可以在这之后通过相关联的std::future<T>对象进行读取。
  2. 一对std::promisestd::future为这一设施提供了一个可能的机制:等到中的线程可以阻塞future,同时提供数据的线程可以使用配对中的promise项,来设置相关的值并使future就绪。
  3. 可以通过get_future()成员函数来获取一个给定的std::promise相关的std::future对象,就像是与std::packaged_task相关。
    1. 当promise的值已经设置完毕,对应期望的状态变为就绪,并且可用于检索已存储的值。
    2. 当在设置值之前销毁std::promise,将会存储一个异常。

  1. promise换句话讲,就是在创建线程的时候,把他作为参数std::move进去,这样子线程可以设置数据或异常给主线程
    1. 需要在std::move之前保存promise对应的future
    2. 并且需要调用futureget方法等待数据
    3. 不过调用了get方法后,主线程会被阻塞,直到子线程里面调用了promiseset_valueset_exception

future保存异常

  1. 传参负数,产生异常

  1. 如果调用square_root函数的是别的线程

  1. 如上述,函数作为std::async的一部分时,当在调用抛出一个异常,这个异常就会存储到期望的结果数据中,之后期望的状态被置为就绪,之后调用get函数时就会抛出这个存储的异常。
    1. 将函数打包入std::packaged_task任务包中后,这个任务被调用时,同样会发生,当打包函数抛出一个异常,这个异常会被存储到期望的结果中,准备在调用get时再次抛出
  2. 使用异常填充promise

多个线程的等待

  1. 虽然std::future可以处理所有在线程间数据转移的必要同步,但是调用某一特殊std::future对象的成员函数,就会让这个线程的数据和其他线程的数据不同步
    1. 当多个线程没有额外同步的情况下,访问一个独立的std::future对象时,就会有数据竞争和未定义的行为,这是因为:std::future模型独享同步结果的所有权,并且通过调用get函数,一次性的获取数据,这就让并发访问变得没有意义了(只有一个线程可以获取结果值,因为在第一次调用get之后,就没有值可以获取了)
  2. std::shared_future可以解决让多个线程等待同一个事件。
    1. 在每一个std::shared_future的独立对象 上成员函数调用返回的结果还是不同步的,所以为了在多个线程访问一个独立对象时,避免数据竞争,必须使用锁来对访问进行保护
    2. 优先使用的方法:
      为了替代只有一个拷贝对象的情况,可以让每个线程都拥有自己对应的拷贝对象。这样,当每个线程都通过自己拥有的std::shared_future对象获取结果,那么多个线程访问共享不同结果就是安全的。

限定等待时间

时延超时方式

  1. 指定需要一段时间,如30毫秒

绝对超时方式

  1. 指定一个时刻

有时间限制的等待

  • condition_variable有两个重载的wait_for成员函数和两个重载的wait_until成员函数

时钟

  1. 当前时间
    1. std::chrono::system_clock::now()
    2. 非稳定的,因为时钟可调,也就是这种是完全自动适应本地账户的调节
  2. std::chrono::steady_clock
    1. 稳定的
  3. std::chrono::hig_resolution_clock
    1. 最小节拍周期,因而最高的精度的时钟

时延

  1. std::chrono::duration<>函数模板能够对时延进行处理
    1. 第一个参数是类型表示
    2. 第二个参数是所用秒数

时间点

实例

可接受超时的函数

本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

bingliaolong
Bingliaolong 关注:0    粉丝:0 最后编辑于:2023-09-16
Everything will be better.

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享