C++ 多线程中的条件变量

2025年3月21日 | 阅读 4 分钟

在本文中,我们将讨论 C++ 多线程中的条件变量。但在讨论条件变量之前,我们必须了解多线程。

什么是多线程?

多线程是计算机科学和软件开发中的一个基本概念。它涉及在单个进程中同时运行多个线程。程序中最小的执行单元称为线程,它们本质上是独立、并发的指令序列。多线程使程序能够同时执行多项活动,从而提高响应能力、性能和资源效率。

多线程的重要元素

Thread

  • 线程是进程中的一组指令。进程内的多个线程可以有效地相互通信和交互,因为它们共享相同的内存和资源。

进程 vs. 线程

  • 进程是当前正在运行的程序的单个实例,包括其数据、代码和系统资源。
  • 单个进程中可以存在多个共享相同内存空间的线程。
  • 与在不同进程中拥有自己内存空间的线程相比,同一进程中的线程可以更容易地通信。

并发 vs. 并行

  • 并发是系统同时处理多项任务的能力。
  • 操作系统的快速上下文切换可能会导致线程看起来是并发运行的,即使它们可能不是同时执行的。
  • 相反,并行性涉及线程的实际同时执行,通常需要使用多个 CPU 核心。

条件变量概述

条件变量是同步原语,允许线程暂停执行,直到满足给定条件。它们与互斥锁结合使用时,可有效管理共享资源并同步线程。

C++ 中的条件变量通常与 C++ 标准库中 头文件中的 std::condition_variable 类一起使用。它们为线程提供了一种有效的方法来等待条件变化,而不会浪费 CPU 周期。

工作机制

与条件变量相关的核心操作是 wait、notify_one 和 notify_all 方法。这些例程与互斥锁结合使用,根据预定义的条件协调线程。

1. 等待(Wait)

  • 当调用线程收到来自另一个线程的 notify_one 或 notify_all 调用时,wait 函数会自动释放关联的互斥锁并使其进入休眠状态。
  • 等待线程在重新获得互斥锁后从 wait 函数返回。

2. notify_one 和 notify_all

  • notify_one 和 notify_all 函数允许一个(如果有)停留在条件变量上的等待线程通过唤醒它来恢复执行。
  • 另一方面,notify_all 会唤醒所有停留在条件变量上的等待线程。

程序

让我们举一个例子来说明 C++ 中的条件变量

输出

Condition met. Proceeding with execution.

条件变量

  • 条件变量的目标是帮助线程在特定情况下协调它们的执行。

用途

  • 它们通常与互斥锁结合使用以同步对共享数据的访问。
  • 与条件变量相关的基本函数是 wait、notify_one 和 notify_all。

互斥锁

  • 互斥锁(mutual exclusion)的目标是保证线程对共享资源的独占访问。

用途

  • 它保证一次只有一个线程读取代码的关键部分,并阻止数据竞争。

等待逻辑

  • waitForCondition 函数中,单个线程等待条件满足。
  • 它使用互斥锁和条件变量 (cv.wait(lock)) 来有效地等待。
  • 等待时,线程会释放互斥锁,允许其他线程使用共享资源。

设置条件逻辑

  • 一段时间后,另一个线程(在 setCondition 函数中)修改条件(ready 标志)。
  • 它使用互斥锁 (std::lock_guard) 来确保在修改共享变量时的独占访问。
  • 修改条件后,它会通知一个等待线程 (cv.notify_one()) 来唤醒它。

主函数

  • 它创建两个线程,一个设置条件,另一个等待条件。
  • 之后,使用互斥锁同步线程对共享变量 (ready) 的访问,并使用条件变量协调线程执行。
  • 本质上,推理建立在线程根据共同条件协调其行为的基础上。
  • 修改条件的线程在互斥锁保护的关键区域内更新共享变量后,通知等待线程 (cv.notify_one)。
  • 等待条件变化的线程将条件变量 (cv.wait) 与互斥锁结合使用。

总结

编写高效且线程安全的并发 C++ 代码需要理解条件变量以及它们与互斥锁的交互方式。另一方面,条件变量的不当使用可能导致死锁或信号丢失等无法检测到的错误。因此,在多线程程序中使用条件变量时,仔细的设计和实现至关重要。

总之,掌握条件变量使 C++ 开发人员能够创建健壮且响应迅速的多线程应用程序,充分发挥并发编程的潜力。