C++并发编程00-并发世界

并发的概念

并发:两个或两个以上的独立活动同时发生。

计算机中的并发:单个系统里同时执行多个独立的任务。

并发与并行:
“并发”指的是代码的性质,强调任务的抽象调度,反义是“顺序”。因此,单核机器也可以通过分时调度和抢占式多任务来实现并发。
“并行”指的是物理运行的状态,强调任务同时执行的真实存在,反义是“串行”。因此,只有多核机器能实现并行。

并发的方式

多进程并发

优点:
更容易编写安全的并发代码,单个进程崩溃波及范围有限,有操作系统帮忙兜底。且独立进程方便改造成远程多机间的并发。

缺点:
进程间有内存空间访问壁垒,通讯需要通过进程间通讯的渠道(信号、套接字、管道、文件、全局共享内存等等),进程间通讯复杂,速度慢,大多数时候需要拷贝。另外多进程有固定开销,需要时间给操作系统准备来启动进程,操作系统需要资源来管理进程。

多线程并发

优点:
所有线程都共享地址空间,并且能访问到大部分数据,全局变量仍然是全局的,指针、对象的引用或数据可以在线程之间快速方便地传递。地址空间共享,以及缺少线程间的数据保护,使得操作系统工作量减小,因此使用多线程的开销远远小于多进程。

缺点:
如果多个线程间需要访问同一地址位置的数据,需要要求开发者来确保每个线程访问到的数据一致且不产生冲突(当然,我们可以参考一些设计方法及引入一些经典库减轻开发者的工作,比如消息队列等等)。

使用并发的原因

关注点分离

使用并发简化成单一关注点,使逻辑更加清晰,任务分离后每个任务职责单一。

提升性能

任务并行 (task parallelism):将一个单任务分成没有互相依赖的多个部分并行运行,从而降低总运行时间。

数据并行 (data parallelism):在以相同算法处理一大块数据时,每个线程在不同的小数据块上执行相同的操作。

阻塞场景性能提升:在阻塞场景下,即使是单核处理器也会有性能的提升。例如起后台线程进行阻塞的I/O操作,这样不会阻塞住主进程或UI进程。

并发虽好,不可滥用

不使用并发的唯一原因:收益比不上成本

都有哪些成本?
编写和维护成本(设计复杂、难以理解、容易出错、维护困难,人工成本);
固有开销(有限的硬件软件资源:内核资源及堆栈内存空间,一个线程一般至少要消耗1MB的空间,线程上下文切换需要耗时等等)。

因此,只有性能关键部分,或关注点分离能让设计足够地清晰和便于维护,才值得并发化。

C++线程库的效率

C++整合底层工具形成线程库的意义:提升编程效率,与其他语言看齐。

事实上,写工具库的意义就在于形成高级工具以提升效率。这就需要了解使用高级工具和使用低级工具的开销差,这个开销差就是抽象代价(abstraction penalty)。

标准库提供了高级别工具,使得编写多线程代码更加简单。因为有额外的代码需要执行,这些工具确实会带来性能开销。总的来说,性能开销和手工编写的函数差不多,并且编译器会内联大部分代码。

绝大多数情况下,用过高的复杂性和过大的出错率,来交换小幅度的性能收益是不划算的。即便是瓶颈出现在C++标准库的工具中,也可能由低劣的程序设计造成。例如,如果过多的线程竞争一个互斥单元,将会很明显的影响性能。与其在互斥操作上耗费时间死磕去裸写底层代码,不如重新设计,减少互斥单元上的竞争。


本文为原创内容,遵循CC BY-ND 4.0协议,署名-禁止演绎。
转载请注明出处:https://tis.ac.cn/blog/kongdeyou/cpp_concurrency_00/

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注