CPP-Concurrency-01-概述

前面学了 C的基本概念,由于并发编程内容比较多,这里单独学习《C Concurrency In Action - second Edition 2019》并发编程

问题 1:什么是并发

在一个双核机器(具有两个处理核心)上,每个任务可以在各自的处理核心上执行。在单核机器上做任务切换时,每个任务的块交织进行。单核交织进行任务的时候中间有一小段分隔(任务切换也是需要时间的)

为了实现交织进行,系统每次从一个任务切换到另一个时都需要切换一次上下文(context switch),进行上下文的切换时,操作系统必须为当前运行的任务保存CPU的状态和指令指针,并计算出要切换到哪个任务,并为即将切换到的任务重新加载处理器状态。然后,CPU可能要将新任务的指令和数据的内存载入到缓存中,这会阻止CPU执行任何指令,从而造成的更多的延迟。

Snipaste_2023-08-15_00-05-38

Snipaste_2023-08-15_00-10-45

问题 2:如何实施并发

  • 第一种,每个进程只要一个线程,同时运行多个进程。进程中的所有线程共享地址空间,需要保证数据一致性
  • 第二种,每个进程有多个线程。独立进程可以通过进程间常规的通信方式(信号、套接字、文件、管道)。缺点是 相比线程间通信,它的速度慢,每个进程所需的固定开销(时间启动、内部资源管理进程)。但是编码更容易切安全

问题 3:什么时候不用并发

不使用并发的唯一原因就是收益比不上成本。使用并发的代码在很多情况下难以理解,因此编写和维护的多线程代码就会直接产生脑力成本,同时额外的复杂性也可能引起更多的错误。

例子

1
2
3
4
5
6
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World\n";
}

与启动一个独立的线程

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <thread>
using namespace std;
void hello()
{
cout << "Hello Concurrent World\n";
}
int main()
{
thread t(hello); // 2
t.join(); // 3
}

序号说明:

  1. 标准C++库中对多线程支持的声明在新的头文件中
  2. 每个线程都必须具有一个初始函数(initial function),新线程的执行从此开始。对于应用程序来说,初始线程是main(),对于其他线程,可以在 std::thread 对象的构造函数中指定
  3. 初始线程拉起新线程之后,会同时继续执行。如果不等待新线程结束,就将自顾自地继续运行到main()的结束,有可能发生在新线程运行之前。所以调用 join() 的等待新线程结束

这里包含了一些问题,待后续解答

  1. 进程与线程有什么区别
  2. 创建线程的时候发生了什么
  3. 线程是如何管理的
  4. 线程是如何通信的

参考链接

  1. 《C++ Concurrency In Action - second Edition 2019》