FOUR:tonbu 并发操作
发布日期:2021-06-29 18:56:11 浏览次数:2 分类:技术文章

本文共 2003 字,大约阅读时间需要 6 分钟。

文章目录

  • 等待事件
  • 用 future来等待一次性事件
  • 有时间限制的等待
  • 用操作的同步来简化代码

  • 上章
    • 各种方法保护在线程间共享的数据
  • 但有时你不只需保护数据
    • 还需在独立的线程上同步操作
  • 一个线程在能够完成其任务之前可能需等待另一个线程完成任务
  • 希望一个线程等待特定事件的发生或
    • 是一个条件变为true是常见的事情
  • 虽然通过定期检查“任务完成”的标识或是在共享数据中存储类似的东西也能够做到这一点,但不理想
  • 这样的线程间同步操作的需求常见
    • C++标准库提供以条件変量和期值为形式的工具来处理它

  • 如何用条件变量和期值来等待事件
  • 如何用它们来简化操作的同步

4.1等待事件或其他条件

  • 你正乘坐通宵列车旅行。
  • 理想的状况是,你只管去睡觉,让某个人或某个东西在火车到站时叫醒你,无论何时

  • 如果一个线程正等待着第二个线程完成一项任务,
    • 它有几个选择
  • 它可一直检查共享数据(由互斥元保护)中的标识
    • 且让第1个线程在完成任务时设置该标识
  • 两浪费,线程占用了宝贵的处理时间去反复检查该标识
    • 当互斥元被等待的线程锁定后,就不能被任何其他线程锁定。
  • 两者都反对线程进行等待,
    • 它们限制了等待中的线程的可用资源,
    • 甚至阻止它在完成任务时设置标识
  • 整夜保持清醒地与司机交谈,
    • 他不得不把火车开得更慢,
    • 因为你一直在干扰他,所以需更长的时间オ能到达
  • 等待中的线程消耗了本可以被系统中其他线程使用的资源,
    • 且最终等待的时间可能会比所需的更长

  • 第二个选择
  • 用std::this_thread::sleep_for()(4.3节)
  • 让等待中的线程在检査之间休眠一会儿。
  • 这个循环里,函数在休眠之前②解锁该互斥元,并在之后再次锁定之③,所以另一个线程有机会获取它并设置标识。

  • 线程在休眠时并不浪费处理时间,但得到正确的休眠时间是很难的。
  • 检查之间休眠得过短,线程仍然会浪费处理时间进行检查;
  • 休眠得过长,即使线程正在等待的任务已经完成,它还会继续休眠,导致延迟。
  • 这种过度休眠很少直接影响程序的操作,但它可能意味着在快节奏的游戏中丢帧,或者在实时应用程序中过度运行一个时间片。

  • 用C标准库提供的工具来等待事件本身
  • 等待由另一个线程触发一个事件的最基本机制
    • (如前面提到的管道中存在的额外操作)
    • 是条件变量
  • 条件变量与某些事件或其他条件相关
    • 且一个或多个线程可等待该条件被满足
  • 当某线程已经确定条件得到满足
    • 它就可通知一个或多个正在条件变量上等待的线程
    • 唤醒它们并让它们继续

4.1.1用条件变量等待条件

  • 标准C++库提供两个条件变量的实现

    • condition_variable
    • condition_variable_any。
    • <condition_variab1e>头文件
  • 两者都需和互斥元一起工作,以便提供恰当同步

  • 前者仅限和std::mutex一起工作

  • 后者

    • 可与符合成为类似互斥元的最低标准的任何东西一起工作
    • 所以有大小、性能或者操作系统资源方面的形式的额外代价的可能
  • 首选前,除非需要额外灵活性

  • 怎么让正在等待工作的线程休眠,直到有数据要处理?
  • 清单4.1展示一种方法,
    • 你可用条件变量来实现这一点

  • 一个用来在两个线程之间传递数据的队列
  • 数据就绪时,
    • 准备数据的线程用std:lock_guard
    • 去锁定保护队列的互斥元,且将数据压入队列2
  • 它在condition_variable的实例上调成员函数,
    • 通知等待中的线程(如果有的话)

  • 你还有处理线程
  • 先锁定互斥元,但用std: unique_lock
    • 很快就会明白为什么
  • 接下来在std: condition variable上调wait()
    • 传入锁对象
    • 以及表示正在等待的条件的 lambda函数5
  • lambda函数允许你编写一个匿名函数作为另一个表达式的一部分,
    • 适合于为类似于wait(O这样的标准库函数指定断言。
  • 5
    • 检査 data queu是否不为 empty()
    • 即队列中已有数据准备处理
  • A.5节更加详细地描述lambda函数

  • wait()的实现
    • 接下来检査条件(lambda函数),满足时返回(lambda返true)
  • 如果条件不满足
    • wait()解锁互斥元,并将该线程置于阻塞或等待状态
  • 当来自数据准备线程中对notify one()的调用通知条件变量时
    • 线程从睡眠状态中苏醒(解除其阻塞),重新获得互斥元上的锁,
    • 再次检査条件,如果条件已经满足,就从wait()返回值,互斥元仍被锁定
    • 如果条件不满足,该线程解锁互斥元,并恢复等待
  • 这就是为什么要unique_lock而不是
    • 等待中的线程在等待期间必须解锁互斥元,
    • 并在这之后重新将其锁定lock_guard没有提供这样的灵活性
  • 如果互斥元在线程体眠期间始终被锁定,
    • 数据准备线程将无法锁定该互斥元,
    • 以便将项目添加至队列,
    • 且等待中的线程将永远无法看到其条件得到满足

转载地址:https://cyj666.blog.csdn.net/article/details/109628196 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:宝5:16 OS
下一篇:处理器的问题 VS 应用程序自己的问题

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2024年04月03日 23时03分22秒