C++11 及之后的标准引入了新的线程库。该库包含启动和管理线程的工具,还提供互斥量(mutex)等锁之类的同步原语,以及原子变量和相关工具。
在 C++ 中拥有一套标准化的并发管理方式是一项重大改进。在这些特性被引入之前,C++ 项目依赖各种各样的库和方法来处理并发。
在本文中,我们将谈谈阅读 Rainer Grimm 所著《Concurrency with Modern C++》一书的好处。
务实的方法
在计算机科学领域,有些书以学术为主、面向学生,而另一些书则面向开发者——他们在构建产品时需要解决现实问题的实用指导。本书遵循务实的方法,从一开始就引用了示例代码包,并解释如何编译它们。其目标是让读者动手实践书中提供的每一个解决方案。
从一开始就展现全貌
一般来说,我们可以通过查看目录和阅读序言来了解一本书的内容。然而,在许多情况下,这并不足以让人清楚地了解这本书的目标。
这本书的有趣之处在于,它有一个 8 页的“快速概览”(Quick Overview)章节,清晰地解释了问题、解决方案以及新标准提供的特性。
清晰讲解内存模型
从快速概览开始,内存模型的约束就被清晰地解释了。
多线程的基础是一个定义良好的内存模型。这个内存模型必须处理以下几个方面:• 原子操作:可以不被中断地执行的操作。• 操作的偏序关系:不得被重排的操作序列。• 操作的可见效果:保证对共享变量的操作何时对其他线程可见。
在专门讲解内存模型的章节中,作者对内存约束进行了深入分析,并配以大量代码示例,让读者更容易理解这些概念。
这一章还介绍了较新的标准为处理并发程序中的内存问题所提供的特性。
揭开多线程的神秘面纱
太多编写多线程程序的程序员学会了创建一堆线程并让它们勉强运转,但随后线程就完全失控,程序员不知所措。在那之后,修复缺陷或添加代码都成了头疼的事。有时修复一个缺陷会引出许多其他缺陷。
因此,建议在把多线程用于生产代码之前,先掌握它的基础。幸运的是,自 C++11 起,C++ 就有了多线程接口,而且这个接口具备创建多线程程序所需的全部基本构件。拥有一套标准化的线程处理方式总是更好的。
本书“多线程”章节的一个优点是其循序渐进的方式,这让它对尚不熟悉线程的初学者也同样有用。我也会把这一章推荐给有经验的 C++ 开发者。就我个人而言,即使与线程打了多年交道,我仍然发现了几个有趣的细节和新特性。
与内存模型章节一样,大量的代码示例和插图让多线程概念更容易理解。
通过案例研究实践理论
在讲解完内存模型和多线程接口的理论之后,作者将其付诸实践,并给出了一些性能数据。
这些案例研究很容易上手,比如计算向量元素之和的那个案例。作者还深入讲解了用现代 C++ 特性实现它的各种不同方案。
读完并动手实践第一个案例研究之后,您将对许多并发问题及其应对方法有切实的理解。我向任何想掌握 C++ 并发的开发者推荐这个案例研究。
最后的挑战与最佳实践
在讲解完内存模型和多线程、并通过案例研究加以巩固之后,作者给出了几个有趣的挑战。下面是他的动机:
并发编程本质上是复杂的。如果您使用 C++11 和 C++14 的特性,这一点尤其如此。我甚至还没有谈到内存模型。我希望,如果我用整整一章来讲述并发编程的挑战,您可能会对这些陷阱有更清醒的认识。
作者还给出了几条应对并发的最佳实践,正如他所解释的:
本章为您提供了一套简单的规则,用于用现代 C++ 编写定义良好且快速的并发程序。多线程——特别是并行与并发——在 C++ 中是一个相当新的话题;因此,未来几年会有越来越多的最佳实践被发现。请不要把本章的规则视为一份完整的清单;而应把它看作一个会随时间演进的必要起点。
结语
阅读本书的过程中可以明显看出,作者是一位在 C++ 项目中解决实际问题方面经验丰富的程序员,这正是本书务实风格的由来。我们向任何想掌握现代 C++ 并发的开发者推荐这本书。
