C++:线程库的使用
文章目录
- Windows和Linux平台的线程
- 线程
- 构造函数
- 模板参数包
最近发现C++11的线程库还没有进行总结,因此本篇对于C++11当中新增的线程库的一些基本用法进行总结
Windows和Linux平台的线程
在Linux平台下是存在一些原生的线程系统调用的,比如有pthread_create这样的系统调用,而在C++11标准之后,C++自己的标准库中也出现了有关线程的调用库,这样最大的好处就是只要使用的是C++提供的线程库,这样不管是在Linux下还是在Windows下都是可以使用的,提供了一个跨平台的好处,因为这本身是属于语言本身的
对于线程库当中的实现,其实就是把这样的类unix平台的接口和Windows的原生接口进行了一个条件编译,当检测到时WIN32的系统的时候,就使用的是Windows提供的原生的线程,而如果识别到时unix的系统,就使用unix提供的线程,具体可以理解为下面这样
#ifdef _WIN32
CreateThread()
#else
pthread_create()
#endif
线程
C++语言本身提供的这个线程,是一个用类进行封装的线程:
也提供了一些成员函数
构造函数
对于一个类来说,首先要看一下它的构造函数,看一下可以用什么样的方法来构造出这个函数
在上面的这四种构造函数中出现了一些熟悉的身影,默认构造函数是一个无参的构造函数,表示的是一个空线程,不启动,其次是一个模板,在这个模板中带有的是一个可变参数包,并且还有万能引用等,这些后续进行代码实践,需要注意的是其中的Fn表示的是一个可执行对象,可以是函数指针,仿函数,lambda表达式,包装器等等
从下面的拷贝函数可以看出,线程不支持拷贝构造,但是支持移动构造
模板参数包
为什么要诞生出这个模板参数包来进行构造函数?先看看Linux下的线程创建是怎么创建的
如图所示的是Linux下的线程创建的系统调用,第一个参数是线程的tid,这里就不多介绍了,重点是后面的函数指针,后面的arg表示的是函数指针的参数,那这就意味着会用到强转这样的信息来进行参数的解析,如果使用原生的方式要传递多个参数,通常要把这些信息放到一个结构体当中:
#include <iostream>
#include <unistd.h>
#include <string>
using namespace std;struct thread_data
{thread_data(const string &message, int id) : _message(message), _id(id) {}string _message;int _id;
};void *routine(void *arg)
{struct thread_data *td = (struct thread_data *)arg;cout << td->_message << " : " << td->_id << endl;return nullptr;
}int main()
{pthread_t tid;for (int i = 0; i < 5; i++){thread_data td("这是线程", i);pthread_create(&tid, nullptr, &routine, (void *)&td);pthread_join(tid, nullptr);}return 0;
}
而在C++11的库当中,如果使用这个模板参数包,就可以很方便的直接进行使用了:
#include <iostream>
#include <thread>
#include <windows.h>
#include <functional>
using namespace std;void func1(const string& message, int id)
{cout << message << " " << id << endl;
}struct func3
{void operator()(const string& message, int id){cout << message << " " << id << endl;}
};int main()
{// 使用函数指针string tmp1 = "这是函数指针的方法";int id1 = 1;thread t1(&func1, tmp1, id1);Sleep(1000);// 使用lambda表达式string tmp2 = "这是lambda的方法";int id2 = 2;thread t2([&](const string& message, int id) {cout << message << " " << id << endl;}, tmp2, id2);Sleep(1000);// 使用仿函数string tmp3 = "这是仿函数的方法";int id3 = 3;func3 f3;thread t3(f3, tmp3, id3);Sleep(1000);// 使用包装器function<void(const string&, int)> func4 = func1;string tmp4 = "这是包装器的方法";int id4 = 4;thread t4(func4, tmp4, id4);Sleep(1000);// 回收等待t1.join();t2.join();t3.join();t4.join();return 0;
}
如上所示是四种可执行方式调用线程的方式