第一章 并发世界 Hello, World
June 25, 2026About 4 min
第一章 并发世界 Hello, World
何谓并发(concurrency)?
在生活中, 最简单和基本的并发是指两个或更多独立的活动(separate activities)同时发生.
计算机领域的并发指的是在单个系统里并行执行多个独立的活动(independent activities),而不是顺序执行或一个接一个地执行.
单核计算机一次只能执行一个任务, 可以通过每秒多次任务切换(task switching)造成“并发的假象”.

每次任务切换时都需要切换上下文(context switch),是需要时间的, 如上图.
- 切换上下文时,操作系统必须为当前运行的任务保存CPU的状态和指令指针,并计算出要切换到哪个任务,并为即将切换到的任务重新加载处理器状态。
- 然后CPU可能要将新任务的指令和数据的内存载入到缓存中,这会阻止CPU执行任何指令,从而造成的更多的延迟。
Info
因为计算机要执行的任务远超过硬件并发的数量, 所以任务切换依然是必须的.
并发的途径(Approaches to concurrency)
多进程并发
将应用程序分为多个独立的进程,它们在同一时刻运行,就像同时进行网页浏览和文字处理一样.

独立的进程可以通过进程间常规的通信渠道传递讯息(信号、套接字、文件、管道等等).
Info
优点
- 操作系统在进程间提供附加的保护操作和更高级别的通信机制,意味着可以更容易编写安全的并发代码
- 可以使用远程连接(可能需要联网)的方式,在 不同的机器上运行独立的进程.
缺点
- 进程间通过外部通信复杂度高, 速度慢。
- 需要时间启动进程, 操作系统需要内部资源来管理进程,等等。
多线程并发
在单个进程中运行多个线程。线程很像轻量级的进程:每个线程相互独立运行,且线程可以在不同的指令序列中运行。
进程中的所有线程都共享地址空间, 并且所有线程访问到大部分数据---全局变量仍然是全局的,指针、对象的引用或数据可以在线程之间传递.

为什么使用并发?
There are two main reasons to use concurrency in an application: separation of concerns and performance.
为了分离关注点
- 通过将相关的代码与无关的代码分离,可以使程序更容易理解和测试,从而减少出错的可能性。
- 即使一些功能区域中的操作需要在同一时刻发生的情况下,依旧可以使用并发分离不同的功能区域。
- 独立的线程通常可用来执行那些必须在后台持续运行的任务,如,桌面程序中监视文件系统变化的任务。
为了性能
- 如果软件想要利用日益增长的计算能力,那就必须设计多任务并发式。
If software is to take advantage of this increased computing power, it must be designed to run multiple tasks concurrently。 - 两种方式利用并发提高性能:
- 将一个单个任务分成几部分,且各自并行运行,从而降低总运行时间。这就是**任务并行(**task parallelism).
- 每个线程对数据的不同部分执行相同的操作。这种方法被称作数据并行(data parallelism).
例如,并行处理图片的各部分,就能提高视频的分辨率。
什么时候不使用并发
不使用并发的唯一原因就是,收益比不上成本。
- 除非潜在的性能增益大或关注点分离清晰,否则不要使用并发。
- 线程启动存在固有开销,太多线程会消耗操作系统资源,导致整体运行缓慢。
- 每个线程需要独立堆栈空间,太多线程会耗尽内存或地址空间。
- 运行更多线程会导致更多上下文切换,耗费有价值工作的时间。
Hello, Concurrent World
#include <iostream>
#include <thread>
void hello() //1
{
std::cout << "Hello Concurrent World\n";
}
int main()
{
std::thread t(hello); //2
t.join(); //3
}
- 每个线程都必须具有一个初始函数(initial function),新线程的执行从这里开始。
对于应用程序来说,初始线程是main()函数。但对于其他线程,可以通过在std::thread对象的构造函数中指定初始函数。例子中是hello函数。 - 当一个新线程被创建并启动后,原来的主线程(也称为初始线程)会继续执行。这是并发的特点,两个线程可以同时运行。
- join() 让主线程等待线程t执行完毕后再继续执行。避免了新线程成为僵尸线程—已经启动但没有任何操作就结束的线程。
关于join的细节描述请看第二章。