C++11多线程编程


进程与线程

  • 进程就是正在运行的程序
  • 线程就是进程中的进程
  • 多线程可以提高效率。
  • 线程的多少取决于CPU的核数
    如下图,如果是串行的话就必须顺序执行,但如果并行的话你就可以在同一时间内干多个事情。但同时要注意你的“核数”,你不能同时刷抖音和听音乐,因为你没有四个耳朵,亦或者说你的手机同一时间内只能处理一个应用播放声音。
    图片

C++11 Thread

  • 准备工具 Visual Studio
  • 线程库 thread
1
2
#include <iostream>
#include <thread>

使用thread创建一个线程

1
2
3
4
5
6
7
8
9
void PrintHelloWorld()
{
std::cout<<"HelloWorld"<<std::endl;
}
int main()
{
std::thread thread1(PrintHelloWorld);
return 0;
}

运行调试后,会得到下面结果:
图片
我们发现报错了,为什么会这样呢?


可以理解为main里是主线程,一开始我们便创建了一个子线程,并让其执行PrintHelloWorld的函数。当子线程没有执行完毕的时候,主线程已执行完毕。主线程不会等待子线程执行完毕,可能子线程才执行到Hello,主程序已经return 0了,所以出现报错;所以说主线程和子线程是并发运行的!

1
std::cout<<"HelloworldMain"<<std::endl;

当我们再次加入一行代码时发现,先输出的是主线程的HelloworldMain,接下来才是子线程的Helloworld。从这里我们可以看出:创建线程是需要时间的
图片

如何解决主线程和子线程先后结束时间不同而引起的程序崩溃?

函数thread.join()

在 C++ 中,std::thread::join() 的作用是 等待子线程执行完毕,然后主线程才继续执行。如果没有 join(),主线程可能会在子线程执行完成之前就结束,导致未定义行为程序报错。

1
2
3
4
5
std::thread thread1(PrintHelloWorld);
thread1.join();
std::cout << "helloworldMain" << std::endl;
system("pause");
return 0;

当我们添加了thread1.join()之后,相当于告诉主线程:“大哥你先等我弄完你再往下走,别丢下我!” (演示注释掉thread1.join()的情况
join的作用是:确保子线程完成,执行顺序可控,但是会阻塞主线程,直到子线程完成。
阻塞示例 (joinable()用于判断线程是否可以使用join函数,返回的是一个bool值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <thread>

void print()
{
for (int i = 0; i < 100000; i++) {
std::cout << i;
}
std::cout << std::endl;
}

int main()
{
std::thread thread1(print);
if (thread1.joinable())
{
thread1.join();
}
std::cout << "over";
system("pause");
return 0;
}

函数thread.detach()

让子线程“脱离”主线程,主线程不再管理它。

1
2
3
4
5
6
std::thread thread1(PrintHelloWorld);
thread1.detach();
system("pause");
std::cout << "1";
system("pause");
return 0;

你可以干你的事情,我也可以干我的事情,不管你了。像请客吃饭,请客的人把单买了之后说我先走一步,你们随意。对比上面的阻塞,我们发现detach是并发的。