C++并发编程指南01

发布时间:2026/7/12 14:56:25
C++并发编程指南01 文章目录1.1 何谓并发1.1.1 计算机的并发1.1.2 并发的方式多进程并发多线程并发1.1.3 并发与并行精简后的文章1.1 什么是并发1.1.1 计算机中的并发1.1.2 并发的两种方式多进程并发多线程并发1.1.3 并发与并行1.1 何谓并发并发指两个或两个以上的独立活动同时发生。并发在生活中随处可见我们可以一边走路一边说话也可以两只手同时做不同的动作还有每个人都过着相互独立的生活——当我在游泳的时候你可以看球赛等等。1.1.1 计算机的并发计算机的并发指在单个系统里同时执行多个独立的任务。并发在计算机领域不是一个新鲜事物。很多年前一台计算机就能通过多任务操作系统的切换功能同时运行多个应用并且多处理器服务器很早就实现了并行计算。那并发为什么在计算机领域越来越流行呢——真正的并行。以前大多数计算机只有一个处理器具有单个处理单元或核芯。这种机器只能在某一时刻执行一个任务不过可以在单位时间内对任务进行多次切换。通过“这个任务做一会另一个任务再做一会儿”的方式让任务看起来是并行的这种方式称为任务切换。如今这样的方式仍称为“并发“因为任务切换得太快以至于无法感觉到任务会暂时挂起。任务切换会给用户造成一种“并发的假象”任务切换和真正并发执行相比行为上还是有着微妙的不同。多核计算机用于高性能计算已有多年。基于单芯多核处理器(多核处理器)的台式机也越来越大众化。无论有多少个处理器这些机器都能够真正的并行多个任务我们称其为”硬件并发“。图1.1显示了处理两个任务时的理想情景每个任务被分为10个相等大小的子任务块。在一个双核机器上每个任务可以在各自的处理核心上执行。在单核机器上做任务切换时每个任务的块交替进行但中间有一小段分隔(图中所示灰色分隔条)这表示切换任务的开销。进行任务切换时操作系统必须保存当前任务CPU的状态和指令指针并计算要切换到哪个任务再将切换到的任务加载处理器中。CPU可能要将新任务的指令和数据载入到缓存这会让CPU停止执行指令从而造成的更多的延迟。图 1.1 并发的两种方式真正并行 vs. 任务切换有些处理器可以在一个核心上执行多个线程但硬件并发在多处理器上效果更加显著。硬件线程最重要的是数量也就是可以并发运行独立任务的数量。即便是硬件并发的系统也有比硬件“可并行最大任务数”还要多的任务需要执行所以任务切换在这些情况下仍然适用。例如一个台计算机上可能会有成百上千个的任务在运行即便是在计算机处于空闲时还是会有后台任务在运行。正是任务切换使得这些后台任务可以运行这样系统使用者就可以同时运行文字处理器、编译器、编辑器和Web浏览器了。图1.2显示了四个任务在双核处理器上的任务切换仍是将任务整齐地划分为同等大小子任务块的理想情况。实际上许多因素会使得任务分割不均或调度不规则。图 1.2 四个任务在两个核心之间的切换无论应用是在单核处理器还是多核处理器上运行不论是任务切换还是硬件并发这里提到的技术、功能和类(本书所涉及的)都会涉及。如何使用并发很大程度上取决于可用的硬件并发。1.1.2 并发的方式试想当两个程序员在两个独立的办公室一起做一个软件项目他们可以安静地工作、互不干扰并且人手一套参考手册。但沟通起来就有些困难比起可以直接交谈他们必须使用电话、电子邮件或到对方的办公室进行面对面交流。并且管理两个办公室需要有一定的经费支出还需要购买多份参考手册。假设让开发人员同在一间办公室办公他们可以自由的对某个程序设计进行讨论也可以在纸或白板上绘制图表对设计观点进行辅助性阐释。现在只需要管理一个办公室和一套参考资料就够了。遗憾的是开发人员可能难以集中注意力并且还可能存在资源共享的问题(比如“参考手册哪去了?”)以上两种方法描绘了并发的两种基本途径。开发人员代表线程办公室代表进程。第一种方式是每个进程只要一个线程这就类似让每个开发人员拥有自己的办公室。而第二种方式是每个进程有多个线程如同一个办公室里有两个开发人员。让我们在一个应用中简单的分析一下这两种方式。多进程并发使用并发的第一种方式是将应用程序分为多个独立的进程同时运行就像同时进行网页浏览和文字处理一样。如图1.3所示独立的进程可以通过进程间的通信渠道传递讯息(信号、套接字、文件、管道等等)。不过这种进程间的通信通常非常复杂或是速度很慢。这是因为操作系统会对进程进行保护以避免一个进程去修改另一个进程的数据。还有一个缺点是运行多个进程的固定开销需要时间启动进程操作系统需要资源来管理进程等等。图 1.3 一对并发运行的进程之间的通信当然以上的机制也不是一无是处操作系统在进程间提供了保护和更高级别的通信机制可以更容易编写安全的并发代码。实际上在类似于Erlang的编程环境中会将进程作为并发的基础块。使用多进程实现并发还有一个优势——可以使用远程连接(可能需要联网)的方式在不同的机器上运行独立的进程。虽然这增加了通信成本但在设计精良的系统中这种低成本方案可提高程序的并行可用性和性能。多线程并发并发的另一个方式在单进程中运行多个线程。线程很像轻量级的进程每个线程相互独立运行并且可以在不同的指令序列中运行。不过进程中的所有线程都共享地址空间并且能访问到大部分数据———全局变量仍然是全局的指针、对象的引用或数据可以在线程之间传递。虽然进程之间通常共享内存但同一数据的内存地址在不同的进程中不相同所以这种共享难以建立和管理。图1.4展示了一个进程中的两个线程正在通过共享内存进行通信。图 1.4 同一进程中一对并发线程间的通信地址空间共享以及缺少线程间的数据保护使得操作系统记录的工作量减小所以使用多线程的开销远远小于多进程。不过共享内存的灵活性是有代价的如果多个线程访问数据那么必须确保每个线程所访问到的数据一致这就需要对线程通信做大量的工作。多个单线程/进程间的通信要比单一进程中多线程通信的开销大若不考虑共享内存可能带来的问题多线程将会成为主流语言(包括C)更青睐的并发方式。此外C标准并未对进程通信提供原生支持所以实现会依赖于平台相关的API。因此本书只关注多线程的并发之后所提到“并发”均为多线程实现。多线程应用中还有一种方式并行。1.1.3 并发与并行对多线程来说这两个概念大部分是重叠的。对于很多人来说它们没有什么区别。这两个词是用来描述硬件同时执行多个任务的方式而“并行”更加注重性能。使用硬件提高数据处理速度时会讨论程序的并行性。当关注重点在于任务分离或任务响应时会讨论程序的并发性。这两个术语存在的目的就是为了区别多线程中不同的关注点。了解了并发后来看看为什么要使用并发。精简后的文章好的我帮你把这段内容优化一下使其更清晰流畅、逻辑层次更分明同时保留技术深度。下面是优化后的版本1.1 什么是并发并发指两个或多个相互独立的活动在同一时间段内发生。在日常生活中并发的例子比比皆是我们可以一边走路一边说话两只手可以同时完成不同的动作当我在游泳时你可能在看球赛——这些都是并发的体现。1.1.1 计算机中的并发在计算机领域并发是指同一系统中同时执行多个独立任务。并发并不是新鲜事物。早期的计算机在单核处理器上就依靠多任务操作系统通过快速的任务切换实现“同时运行”多个应用。例如文字处理器、编辑器和浏览器可以在同一台电脑上看似并行工作。这种方式其实并非真正的并行单核 CPU 任意时刻只能执行一个任务只是通过任务切换time slicing让用户感受到“并发的假象”。切换时操作系统需要保存当前任务的执行状态选择并加载下一个任务同时可能触发 CPU 缓存失效从而带来额外开销。如今多核处理器已成为主流它们可以在硬件层面真正实现并行执行任务。这种能力称为硬件并发。例如在双核 CPU 上两个任务可以各自占用一个核心并同时运行。图 1.1 对比了两种情形单核 CPU 依靠任务切换实现并发存在切换开销多核 CPU 则能够实现真正的并行。即使在多核系统上任务切换依然不可避免通常同时运行的任务数量远远超过硬件可并行的核心数操作系统必须继续通过调度和切换来支持数百甚至上千个任务。1.1.2 并发的两种方式我们可以用一个比喻来说明多进程像两个开发者在两个办公室中工作各自有完整的环境和资源但交流需要电话或邮件开销较大。多线程像两个开发者在同一间办公室中协作沟通高效但容易因共享资源而互相干扰。多进程并发应用程序可以分为多个相互独立的进程通过进程间通信IPC机制交换数据例如信号、管道、套接字或文件。优点操作系统对进程隔离保护避免相互干扰安全性更高可扩展到分布式系统不同进程甚至能运行在不同机器上。缺点进程间通信复杂且开销较大启动和管理多个进程本身也有成本。Erlang 等语言就以进程作为并发的基本单元。多线程并发线程类似“轻量级进程”运行独立的指令序列但共享所属进程的地址空间。优点通信高效共享内存即可创建和切换的开销远小于进程。缺点缺乏内存隔离多个线程访问共享数据时容易出现一致性问题需要同步和互斥机制。在 C 等主流语言中多线程是并发的主要实现方式因为其性能和易用性更优而标准库并未对进程间通信提供原生支持。1.1.3 并发与并行在多线程环境中“并发”和“并行”常常被混用但二者的关注点有所不同并发Concurrency强调任务之间的独立性和相互交错的执行。关注点在于“如何合理安排多个任务”。并行Parallelism强调任务在硬件上真正同时运行。关注点在于“如何利用硬件加速计算”。简而言之并发解决的是结构性问题任务分解与调度并行解决的是性能问题如何更快完成计算。