C++ 多线程编程(一)- C++11中的线程类
1. C++11 多线程编程库
头文件 | 描述 |
---|---|
atomic | 声明了两个类,std::atomic 和 std::atomic_flag ,还声明了一套原子操作函数。 |
thread | 声明了 std::thread 类 |
mutex | 提供互斥锁类,包括 std::mutex 、std::lock_guard 和 std::unique_lock |
condition_variable | 提供条件变量类,包括 std::condition_variable 和 std::condition_variable_any |
future | 提供两个Provider类 std::promise 和 std::package_task ,以及两个 Future 类 std::future 和 std::shared_future ,还有一些相关的函数,例如 std::async |
2. 线程的创建
线程的创建有3种方式:默认构造函数、初始化构造函数 和 移动构造函数
默认构造函数方式和初始化构造函数方式类似,在这里不过多介绍
2.1 初始化构造函数
初始化构造函数其实就是把 线程函数的指针 和 线程函数的参数 一同传入线程类的构造函数中。
#include <iostream>
#include <thread>
using namespace std;
void thread_func_with_arg();
void thread_func_with_one_arg(int arg_1);
void thread_func_with_two_arg(int arg_1, int arg_2);
int main(int argc, char* argv)
{
cout << "//---------------------- program start -----------------------//" << endl;
cout << "----> This is the main thread." << endl;
// 创建线程
thread thread_0(thread_func_with_arg); // 无参数线程
thread thread_1(thread_func_with_one_arg, 1); // 带一个参数线程
thread thread_2(thread_func_with_two_arg, 2, 3); // 带两个参数线程
// 等待线程结束
thread_0.join();
thread_1.join();
thread_2.join();
cout << "//---------------------- program end -----------------------//" << endl;
return EXIT_SUCCESS;
}
void thread_func_with_arg()
{
cout << "--------> This is a thread without id" << endl;
}
void thread_func_with_one_arg(int arg)
{
cout << "--------> This is thread with one arg: " << arg << endl;
}
void thread_func_with_two_arg(int arg_1, int arg_2)
{
cout << "--------> This is thread with two args" << endl
<< " * arg_1: " << arg_1 << endl
<< " * arg_2: " << arg_2 << endl;
}
注意g++编译时,需要加上链接参数 -lpthread.
输出结果为:
//---------------------- program start -----------------------//
----> This is the main thread.
--------> This is a thread without id
--------> This is thread with two args
* arg_1: 2
* arg_2: 3
--------> This is thread with one arg: 1
//---------------------- program end -----------------------//
在这个例子中,分别创建了3个线程,其中一个线程没有输入参数,一个带有一个输入参数,一个带有两个输入参数。
2.2 移动构造函数
现在不太理解这个移动构造函数,也没看出来这个操作具体有什么优势,这里就不写了以免误导大家,想要深入的话可以参考这个:https://blog.youkuaiyun.com/chengqiuming/article/details/89276875
3. 线程的基本操作
3.1 join - 阻塞,等待线程结束
首先我们需要理解:当主线程创建新的子线程后,如果子线程没有被分离(detach),则 当主线程结束的时候,如果子线程还没有结束,则会强制结束子线程。
而join()
函数的功能是 阻塞 调用对象线程(主线程),并 等待子线程结束 ,子线程结束后,再继续执行主线程剩下的任务。
尝试运行下面的程序:
#include <iostream>
#include <thread>
#include <unistd.h>
using namespace std;
void thread_func();
int main(int argc, char* argv[])
{
cout << "//---------------------- program start -----------------------//" << endl;
thread sub_thread(thread_func);
// sub_thread.join(); // 1. 有调用 join() 的时候,程序能没有错误地结束
// 2. 如果不调用 join(), 则在程序结束时,会提示:
// “terminate called without an active exception”
for(int i=0; i<5; i++)
{
cout << "主线程计数: " << i << endl;
usleep(500000);
}
cout << "//---------------------- program end -----------------------//" << endl;
return EXIT_SUCCESS;
}
void thread_func()
{
cout << "--> 子线程开始" << endl;
for(int i=0; i<10; i++)
{
cout << "子线程计数: " << i << endl;
usleep(500000);
}
cout << "--> 子线程结束" << endl;
return;
}
如果注释掉 join()
函数,你会得到类似下面的结果:
//---------------------- program start -----------------------//
主线程计数: 0
--> 子线程开始
子线程计数: 0
主线程计数: 1
子线程计数: 1
主线程计数: 2
子线程计数: 2
主线程计数: 3
子线程计数: 3
主线程计数: 4
子线程计数: 4
//---------------------- program end -----------------------//
子线程计数: 5
terminate called without an active exception
如果你的结果出现如“
主线程计数: 主线程计数: 33
”这样的情况,这是因为在打印的时候没有加 锁 ,关于线程的锁会在后面再提及。
这里可以看到,子线程并没有完全执行(计数到9),而是当主线程结束的时候,就被强制结束了。
而如果调用 join()
的话,结果会是:
//---------------------- program start -----------------------//
--> 子线程开始
子线程计数: 0
子线程计数: 1
子线程计数: 2
子线程计数: 3
子线程计数: 4
子线程计数: 5
子线程计数: 6
子线程计数: 7
子线程计数: 8
子线程计数: 9
--> 子线程结束
主线程计数: 0
主线程计数: 1
主线程计数: 2
主线程计数: 3
主线程计数: 4
//---------------------- program end -----------------------//
这是因为当调用了 join()
之后,主线程会被阻塞,等待子线程完成,然后 join()
函数才会有返回,join()
函数返回后,主线程方可继续执行它自己的任务。
3.2 detach - 分离线程
上面说到利用 join()
函数,可以阻塞主线程,等待子线程完成以后在继续主线程,从而避免当主线程结束的时候强制结束子线程的问题。
那有没有办法可以既不阻塞主线程,又能保证子线程能够在完成后再结束呢?这时就需要使用 detach()
函数。
detach()
函数可以使子线程从主线程中 分离 出来,当主线程结束的时候,不会再强制结束子线程。
将上面程序的 join()
函数换成 detach()
int main(int argc, char* argv[])
{
~~~
thread subThread(thread_func);
subThread.detach();
~~~
}
//---------------------- program start -----------------------//
主线程计数: 0
子线程计数: 0
主线程计数: 1
子线程计数: 1
子线程计数: 2
主线程计数: 2
子线程计数: 3
主线程计数: 3
子线程计数: 4
主线程计数: 4
子线程计数: 5
//---------------------- program end -----------------------//
使用了detach()
函数,当主线程结束后,并没有强制结束子线程(没有报“terminate called without an active exception”),这时其实子线程是在后台继续运行的。
哈,暂时没想到有什么简单的方法证明子线程还在运行,先这样吧。