0%

CPP并发_01你好

C++ 并发编程学习01

为什么需要并发编程?

1. 分离关注点

将相关的代码与无关的代码分离,可以使程序更容易理解和测试,从而减少出错的可能性。使用并发分离出不同的功能区域。例如,独立的线程通常用来执行那些必须在后台持续运行的任务,如,桌面搜索程序中监视文件系统变化的任务。

2. 为了性能

两种方式:

  1. 任务并行: 将一个单一任务分成几部分,且各自并行运行,从而降低总运行时间,每个线程执行不同部分的算法。也被称为 易并行算法

  2. 数据并行: 每个线程在不同的数据部分上执行相同的操作。

3. 什么时候不适用并发?

收益比不上成本,操作系统需要分配内核相关资源和堆栈空间,所以在启动线程时需要开销,然后才能把新线程加入到 调度器 中,这都需要时间。

线程的资源有限。如果太多的线程同时运行,则会消耗很多 操作系统资源 ,从而使得操作系统整体上运行得更加缓慢

每个线程都需要一个 独立的堆栈空间,所以运行太多的线程也会耗尽进程的 可用内存或地址空间

运行越多的线程,操作系统 就需要越多的 上下文切换,每一次切换都需要耗费本可以花在有价值工作上的时间。

并发和多线程

新标准支持并发

Boost线程库作为新类库的主要模型,很多类与Boost库中的相关类有着相同名称和结构。随着C++标准的进步,Boost线程库也配合着C++标准在许多方面做出改变,因此之前使用Boost的用户将会发现自己非常熟悉C++11的线程库。

C++线程库的效率

为了效率,C++类整合了一些底层工具。这样就需要了解相关使用 高级工具 和使用 低级工具 的开销差,这个开销差就是抽象代价(abstraction penalty)。

C++为了提供足够多的底层工具,伴随着新的内存模型,形成了一个综合的原子操作库,可用于直接控制单个位、字节、内部线程同步,以及对所有变化的可见性。

Hello World

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

打印信息的代码被移动到了一个独立的函数中(_2_) 。因为每个线程都必须具有一个初始函数(initial function),新线程的执行从这里开始。

对于应用程序来说,初始线程是main(),但是对于其他线程,可以在std::thread对象的 构造函数 中指定。本例中,被命名为t (_3_)的std::thread对象拥有新函数hello()作为其初始函数。

所以程序启动时,将线程一分为二,初始线程始于 main(),而新线程始于hello()

初始线程继续执行。如果它不等待新线程结束,它就将自顾自地继续运行到main()的结束,从而结束程序——有可能发生在新线程运行之前。这就是为什么在(_4_)这里调用join()的原因——这会导致调用线程(在main()中)等待与std::thread对象相关联的线程,即这个例子中的t