第 18.19 節

并发编程

0瀏覽次數0訪問次數--跳出率--平均停留

本节解决什么问题

现代计算机通常有多个 CPU 核心。一个程序如果只在一个线程里顺序执行,就无法让多个耗时任务同时推进。典型场景包括:

  • 一个线程保持界面响应,另一个线程处理后台任务。
  • 同时等待多个网络请求、定时器或外设事件。
  • 把可拆分的计算任务分给多个线程。

并发编程的难点不在于"开线程"本身,而在于生命周期和共享数据。只要两个线程同时读写同一份数据,就必须认真处理同步问题。

学习路线

本节拆成几个文件级子章节来学:

  • std::thread:创建线程,并用 join() 等待结束。
  • std::mutexstd::lock_guard:保护共享数据。
  • std::atomic:简单计数器的无锁线程安全操作。
  • std::condition_variable:一个线程等待另一个线程发通知。

如果只启动一个任务,同步和并发的结果可能看起来一样。差异通常要在"两个或更多任务同时等待或同时计算"时才明显。这和 Boost.Asio 中一个定时器看不出异步优势、两个定时器才看出区别是同一个道理。

C++ 标准版本

C++11 引入了标准线程库。C++14、C++17、C++20 又增加了 std::shared_mutexstd::jthreadstd::latchstd::barrier 等工具。

本节主要使用 C++11 就有的基础工具。编译时通常需要加线程选项,例如:

g++ demo.cpp -std=c++17 -pthread

常用工具

工具作用常见头文件
std::thread创建和管理线程<thread>
std::this_thread::sleep_for当前线程休眠一段时间<thread><chrono>
std::mutex互斥锁,保护共享数据<mutex>
std::lock_guardRAII 加锁,作用域结束自动解锁<mutex>
std::unique_lock更灵活的 RAII 锁,常配合条件变量<mutex>
std::atomic原子变量,适合简单计数<atomic>
std::condition_variable等待和通知<condition_variable>

并发和并行

概念含义例子
并发多个任务在同一段时间内推进一个程序同时等待网络、定时器和用户输入
并行多个任务真的同时运行多核 CPU 上多个线程同时计算

初学时先抓住工程直觉:多个任务都要等待外设、网络、定时器或耗时计算时,让它们同时推进,程序整体会更快或响应更好。

使用建议

  1. 先保证正确,再考虑性能。
  2. 能不共享数据就不共享数据,每个线程处理自己的数据最简单。
  3. 共享数据优先用 mutex 保护,简单计数才考虑 atomic
  4. 锁的作用域尽量小,避免长时间持锁。
  5. 初学阶段少用 detach(),优先用 join() 明确等待线程结束。
  6. C++20 项目可以了解 std::jthread,它析构时会自动请求停止并 join,比 std::thread 更安全。

工程拓展

在 ROS2 中,多线程回调执行器、回调组和共享状态保护都会涉及线程与锁。在 Boost.Asio 中,多个异步任务可能运行在同一个 io_context 或线程池里,也要考虑回调之间的共享数据。学好 C++ 标准库并发工具,再看这些工程库会轻松很多。

音乐页