1. 并发的形式

任务切换:只有一个处理器的机器在某一时刻执行一个任务,不过它可以每秒进行多次任务切换。通过“这个任务做一会,再切换到别的任务,再做一会儿”的方式,让任务看起来是并行执行的。这种方式称为任务切换(并发的假象)。
硬件并发:拥有几个处理器,这些机器都能够真正的并行多个任务。

1  何谓并发 - 图1

图:真正的并发和任务切换

2. 并发的途径

多进程并发

将应用程序分为多个独立的进程,它们在同一时刻运行,独立的进程可以通过进程间常规的通信渠道传递讯息(信号、套接字、文件、管道等等)。
缺点:设置复杂、速度慢、运行多个进程的固定开销(比如内存,时间开销),OS需要内部资源维护管理进程
优点:OS提供进程间的附加保护,并发代码更安全
1  何谓并发 - 图2
图:并发运行的进程之间的通信

多线程并发

在单个进程中运行多个线程。线程很像轻量级的进程:每个线程相互独立运行,且线程可以在不同的指令序列中运行。所有线程都共享地址空间,并且所有线程访问到大部分数据———全局变量仍然是全局的,指针、对象的引用或数据可以在线程之间传递。
缺点:共享内存难以管理、缺少线程间的数据保护、
优点:开销小,OS工作量减少,是主流语言更青睐的并发途径
1  何谓并发 - 图3
图: 同一进程中的一对并发运行的线程之间的通信

Note: C++标准并未对进程间通信提供任何原生支持,所以使用多进程的方式实现,这会依赖与平台相关的API。
**

3. C++中的并发

C11新标准中不仅有了一个全新的线程感知内存模型,C标准库也扩展了:包含了用于管理线程(参见第2章)、保护共享数据(参见第3章)、线程间同步操作(参见第4章),以及低层原子操作(参见第5章)的各种类。
C14中为并发和并行添加了一个新的互斥量类型,用于保护共享数据(参见第3章)。不过,在C17就考虑的更多了:一开始就添加了一整套的并行算法(参见第10章)。两个标准将整个语言的标准库进行了补强。

一个简单的Hello, Concurrent World程序:

  1. #include <iostream>
  2. #include <thread> // 1
  3. void hello() // 2
  4. {
  5. std::cout << "Hello Concurrent World\n";
  6. }
  7. int main()
  8. {
  9. std::thread t(hello); // 3
  10. t.join(); // 4
  11. }
  • 第一个区别是增加了#include <thread>①。标准C++库中对多线程支持的声明在新的头文件中:管理线程的函数和类在<thread>中声明,而保护共享数据的函数和类在其他头文件中声明。
  • 打印信息的代码被移动到了一个独立的函数中②。因为每个线程都必须具有一个初始函数(initial function),新线程的执行从这里开始。对于应用程序来说,初始线程是main(),但是对于其他线程,可以在std::thread对象的构造函数中指定——本例中,被命名为t③的std::thread对象拥有新函数hello()作为其初始函数。
  • 与直接写入标准输出或是从main()调用hello()不同,该程序启动了一个全新的线程来实现,将线程数量一分为二——初始线程始于main(),而新线程始于hello()。
  • 新的线程启动之后③,初始线程继续执行。如果它不等待新线程结束,它就将自顾自地继续运行到main()的结束,从而结束程序——有可能发生在新线程运行之前。这就是为什么在④这里调用join()的原因,这会导致调用线程(在main()中)等待与std::thread对象相关联的线程,即这个例子中的t。