std::thread
在C++11之前,C++语言层面是不支持多线程的,想利用C++实现并发程序,借助操作系统的API实现跨平台的并发程序存在着诸多不便,当C++11在语言层面支持多线程后,编写跨平台的多线程代码就方便了许多。
C++11提供的std::thread在开发多线程方面带来了便捷。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <iostream> #include <thread> void threadfunc() { std::cout << "thread func" << std::endl; } int main() { std::thread t1(threadfunc); t1.join(); //等待threadfunc运行结束 return 0; }
|
首先定义线程对象t1,线程函数threadfunc运行在线程对象t1中,当线程创建成功并执行线程函数后,一定要保证线程函数运行结束才能退出,这里调用了join()函数阻塞线程,直到threadfunc()运行结束,回收对应创建线程的资源。如果不阻塞线程,就不能保证线程对象t1在threadfunc()运行期间有效,下面不调用join()阻塞线程。
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 threadfunc() { std::cout << "thread func" << std::endl; } int main() { std::thread t1(threadfunc); //t1.join(); //等待threadfunc运行结束 return 0; }
```` 在运行时引起了程序崩溃。 
除了调用join()阻塞线程,保证线程对象在线程函数运行期间的有效性,还可以通过线程分离的手段实现,调用detach()函数使得线程对象与线程函数分离,这样,在线程函数运行期间,线程对象与线程函数就没有联系了,此时的线程是作为后台线程去执行,detach()后就无法再和线程发生联系,也不能通过join()来等待线程执行完毕,线程何时执行完无法控制,它的资源会被init进程回收,所以,通常不采用detach()方法。
|
#include
#include
void threadfunc()
{
std::cout << “ detach thread func” << std::endl;
}
int main()
{
std::thread t1(threadfunc);
t1.detach(); //线程分离
return 0;
}
1 2
| 这里调用detach()实现线程分离,但是运行后,主线程退出的时候threadfunc()还没有输出“detach thread func”,threadfunc()什么时候运行结束也无法确定,为了看到所创建的线程运行结果,在主线程等待一下再退出。
|
#include
#include
#include //时间
void threadfunc()
{
std::cout << “detach thread func” << std::endl;
}
int main()
{
std::thread t1(threadfunc);
t1.detach();
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));//睡眠1000毫秒
break;
}
return 0;
}
detach thread func
1
| 通过std::thread创建的线程是不可以复制的,但是可以移动。
|
#include
#include
#include
void threadfunc()
{
std::cout << “move thread func” << std::endl;
}
int main()
{
std::thread t1(threadfunc);
std::thread t2(std::move(t1));
t2.join();
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));//睡眠1000毫秒
break;
}
return 0;
}
move thread func
1 2 3 4 5 6 7
| 移动后t1就不代表任何线程了,t2对象代表着线程threadfunc()。
## 其他创建线程方式
### 通过std::bind来创建线程函数。
|
#include
#include
#include //时间
#include //std::bind
class A {
public:
void threadfunc()
{
std::cout << “bind thread func” << std::endl;
}
};
int main()
{
A a;
std::thread t1(std::bind(&A::threadfunc,&a));
t1.join();
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));//睡眠1000毫秒
break;
}
return 0;
}
创建一个类A,然后再main函数中将类A中的成员函数绑定到线程对象t1上,运行结果:
### 仿函数对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <thread> #include <iostream>
using namespace std; class Test{ public: void operator()(){ cout << "子线程开始执行!" << endl; //do something cout << "子线程执行完毕!" << endl; }
}; int main() { // thread t(Test()); 这种写法会编译器会认为是一个函数声明,这个函数带有一个参数(函数指针指向没有参数并返回Test对象的函数) thread t((Test())); //可以使用加小括号,或者使用一只初始化,或者传入命名变量 t.join(); //主线程等待子线程 return 0; }
|
### lambda表达式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <thread> #include <iostream>
using namespace std;
int main() { thread t( [] () { cout << "子线程开始执行!" << endl; //do something cout << "子线程执行完毕!" << endl; } ); t.join(); //主线程等待子线程 return 0; }
|
### 非静态成员函数,静态成员函数
以下的下写法中是将对象拷贝了一份副本来创建线程。当我们在进行共享数据的管理时,有时候需要传入对象的指针或者地址。而静态成员函数,不需要传入对象,只需要传入类函数地址。根据C++对象模型,这很好理解因为编译器对非静态成员函数的处理需要this指针,而对静态成员函数的处理不需要。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <thread> #include <iostream>
using namespace std; class Test{ public: void test(){ cout << "子线程开始执行!" << endl; // do somesthing cout << "子线程执行完毕!" << endl; } };
int main() { Test obj; thread t(&Test::test, obj); //注意写法,传入成员函数地址,还需要传入对象 t.join(); //主线程等待子线程 return 0; }
|
## 获取线程id
线程标识类型是 std::thread::id 类型。有两种获取id的方法。
方法一:可以通过调用 std::thread 对象的成员函数 get_id() 来获取id。
方法二:在当前线程中使用std::this_thread::get_id()来获取线程id。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <iostream> #include <thread> #include <chrono> #include <functional> class A { public: void threadfunc() { std::cout << "bind thread func" << std::endl; } }; int main() { A a; std::thread t1(std::bind(&A::threadfunc,&a)); std::cout << "main thread ID is : " << std::this_thread::get_id() << std::endl; std::cout << "t1 thread ID is : " << t1.get_id() << std::endl; t1.join(); while (true) { std::this_thread::sleep_for(std::chrono::milliseconds(1000));//睡眠1000毫秒 break; } return 0; }
|
std::this_thread::get_id()获取的是当前线程的ID,t1.get_id()获取的是所创建的t1对象中运行的线程ID,对应的ID分别为:
1 2 3
| main thread ID is : 11932 t1 thread ID is : 12076 bind thread func
|
虽然get_id()可以获取线程的ID,但是其返回类型是thread::id,通过std::cout可以输出线程ID,但是这样使用似乎不太方面,要是能转换为整形就好了。其实可以将得到的线程ID写入到ostreamstring流中,转换成string类型,再转换成整形。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| #include <iostream> #include <thread> #include <chrono> #include <functional> #include <sstream> class A { public: void threadfunc() { std::cout << "bind thread func" << std::endl; } }; int main() { A a; std::thread t1(std::bind(&A::threadfunc, &a)); std::ostringstream os1; os1 << t1.get_id() << std::endl; std::string strID = os1.str(); //转换成string类型 int threadID = atoi(strID.c_str()); //转换成int类型 std::cout << "t1 thread ID is : " << threadID << std::endl; t1.join(); while (true) { std::this_thread::sleep_for(std::chrono::milliseconds(1000));//睡眠1000毫秒 break; } return 0; }
|
输出结果:
1 2 3
| t1 thread ID is : 6956 bind thread func
|
参考:
https://zhuanlan.zhihu.com/p/157171731
https://www.cnblogs.com/chen-cs/p/13054027.html