:该头文件用于线程操作,主要声明了 std::thread 类,另外 std::this_thread 命名空间也在该头文件中,包含一些线程的操作函数。
:该头文件用于互斥量操作,主要声明了与互斥量相关的类,包括 std::mutex 系列类,std::lock_guard,std::unique_lock,以及其他的类型和函数。
:该头文件用于条件变量操作,主要声明了与条件变量相关的类,包括 std::condition_variable 和 std::condition_variable_any。
:该头文件用于异步调用操作,主要声明了 std::promise,std::package_task 两个 Provider 类,以及 std::future 和 std::shared_future 两个 Future 类,另外还有一些与之相关的类型和函数,std::async() 函数就声明在此头文件中。
:该头文件用于原子操作,主要声明了两个类,std::atomic 和 std::atomic_flag,另外还声明了一套 C 风格的原子类型和与 C 兼容的原子操作的函数。default (1) | thread() noexcept; |
initialization (2) | template explicit thread (Fn&& fn, Args&&… args); |
copy [deleted] (3) | thread (const thread&) = delete; |
move (4) | thread (thread&& x) noexcept; |
除了 join,detach,joinable 之外,std::thread 头文件还在 std::this_thread 命名空间下提供了一些辅助函数:
std::thread类的构造函数是使用可变参数模板实现的,也就是说,可以传递任意个参数,第一个参数是线程的入口函数,而后面的若干个参数是该函数的参数。
我们可以给 std::thread 对象添加函数,这个回调函数将在这个新线程启动时执行。这些回调可以是:
1.) 函数指针
2.) 函数对象
3.) Lambda 函数
//创建 thread 对象:
std::thread thObj(<CALLBACK>);
新线程将在创建新对象后立即启动,并将并行地执行(当参数)传递给线程的回调函数。
#include
#include
#include
using namespace std;
//传入的不是myNum的引用,指向地址不同,实际是值传递,即便主线程detach(),子线程使用num不会出问题
//传入的指针依旧是指向muBuf的地址,所以detach后,子线程会出问题。可以将char*改为const string&
//使用const string&也有问题,即:不知道mybuf不知道什么时候转换为string,如果主线程执行完毕后,还没转换完毕,就有问题
void test(const int& num, char* buf) {
for(int i = 0; i < 5; i++){
cout << "线程1开始执行" << endl;
cout << "num1 = " << num << endl;
cout << "buf1 = " << buf << endl;
cout << "线程1执行完毕" << endl;
}
}
void test2(const int& num, const string& buf) {
for(int i = 0; i < 5; i++) {
cout << "线程2开始执行" << endl;
cout << "num2 = " << num << endl;
cout << "buf2 = " << buf << endl;
cout << "线程2执行完毕" << endl;
}
}
int main() {
int myNum = 1;
int& num = myNum;
char myBuf[] = "only for test!";
const string& myBuf2 = "buf2 only for test!";
thread th(test, num, myBuf);
thread th2(test2, num, myBuf2);
//thread th2(test2,num,string(myBuf));// 针对detach ,生成一个临时string对象,绑定const string&,使用这个方法,就会让string对象在主线程执行完毕前构造 在创建线程的同时构造临时对象是可行的
th.join();
//th2.detach();
th2.join();
cout << "主线程执行完毕" << endl;
return 0;
}
//使用类对象创建线程
#include
#include
using namespace std;
class Test {
public:
Test(){
cout << "Test()构造函数被执行" << endl;
}
Test(const Test& test) {
cout << "拷贝构造函数执行" << endl;
}
~Test() {
cout << "析构函数执行" << endl;
}
void operator()() {//不能带参数
cout << "线程开始执行" << endl;
for (int i = 0; i < 10; ++i);
cout << "线程执行完毕" << endl;
}
};
int main() {
Test test;
thread th(test);
th.detach();
//th.join()
for (int i = 0; i < 10; ++i) {
cout << "主线程执行完毕" << i << endl;
}
return 0;
}
输出
Test()构造函数被执行
拷贝构造函数执行//证明确实复制了一份对象
拷贝构造函数执行
析构函数执行
主线程执行完毕0
主线程执行完毕1
主线程执行完毕2
主线程执行完毕3
主线程执行完毕4
主线程执行完毕5
主线程执行完毕6
主线程执行完毕7
主线程执行完毕8
主线程执行完毕9
析构函数执行//线程还没有执行完毕,所以这个析构是主线程对象
线程开始执行
线程执行完毕
析构函数执行
detach换成join的输出
Test()构造函数被执行
拷贝构造函数执行
拷贝构造函数执行
析构函数执行
线程开始执行
线程执行完毕
析构函数执行//此时主线程还未退出,但该线程执行完毕,所以这个析构对象是析构复制对象的
主线程执行完毕0
主线程执行完毕1
主线程执行完毕2
主线程执行完毕3
主线程执行完毕4
主线程执行完毕5
主线程执行完毕6
主线程执行完毕7
主线程执行完毕8
主线程执行完毕9
析构函数执行//此时主线程退出,所以这个析构对象是主函数中的对象
#include
#include
using namespace std;
class A {
public:
mutable int num;//标注为const也能修改
//类型转换构造函数,可以把一个int转换成一个类A对象
A(int num) :num(num) {
cout << "构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl;
}
A(const A& a) :num(a.num) {
cout << "拷贝构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl;
}
~A() {
cout << "析构函数执行" << this << "threadid=" << std::this_thread::get_id() << endl;
}
};
void test(const A& a) {
a.num = 199;//修改不会影响main的值
cout << "线程test的a.num=" << a.num << endl;
cout << "线程test的参数地址是" << &a << "threadid=" << std::this_thread::get_id() << endl;
}
int main() {
cout << "主线程id是" << std::this_thread::get_id() << endl;
A a(10);
thread th(test, a);//将类对象作为线程参数
th.join();
cout << "主线程的a.num=" << a.num << endl;
cout << "主线程执行完毕" << endl;
return 0;
}
输出
主线程id是140575262807872
构造函数执行0x7ffd04eaf38cthreadid=140575262807872
拷贝构造函数执行0x7ffd04eaf340threadid=140575262807872
拷贝构造函数执行0x56237eb422c8threadid=140575262807872
析构函数执行0x7ffd04eaf340threadid=140575262807872
线程test的a.num=199
线程test的参数地址是0x56237eb422c8threadid=140575262803712
析构函数执行0x56237eb422c8threadid=140575262803712
主线程的a.num=10
主线程执行完毕
析构函数执行0x7ffd04eaf38cthreadid=140575262807872
//将线程传入参数加上std::ref
thread th(test, std::ref(a));
//主线程的a.num = 199,被成功修改
#include
#include
using namespace std;
void test(unique_ptr<int> uptr) {
cout << "子线程id是" << std::this_thread::get_id() << endl;
cout << "当前智能指针的值是" << *uptr << endl;
}
int main() {
cout << "主线程id是" << std::this_thread::get_id() << endl;
unique_ptr<int> uptr(new int(10));
cout << "当前智能指针的值为" << *uptr << endl;
thread th(test, std::move(uptr));//将类对象作为线程参数
th.join();
cout << "主线程执行完毕" << endl;
return 0;
}
若改为detach(),当主线程执行完毕后,uptr被释放,但子线程还在执行,此时就会出现问题
#include
#include
using namespace std;
class A {
public:
mutable int num;
//类型转换构造函数,可以把一个int转换成一个类A对象
A(int num) :num(num) {
cout << "构造函数执行, " << this << "threadid=" << std::this_thread::get_id() << endl;
}
A(const A& a) :num(a.num) {
cout << "拷贝构造函数执行, " << this << "threadid=" << std::this_thread::get_id() << endl;
}
~A() {
cout << "析构函数执行, " << this << "threadid=" << std::this_thread::get_id() << endl;
}
void thread_work(int num) {
num = 199;//修改不会影响main的值
cout << "子线程num=" << num << endl;
cout << "子线程thread_work执行了, " << "threadid=" << std::this_thread::get_id() << endl;
}
};
void test(const A& a) {
a.num = 199;//修改不会影响main的值
cout << "a.num=" << a.num << endl;
cout << "线程test的参数地址是" << &a << "threadid=" << std::this_thread::get_id() << endl;
}
int main() {
cout << "主线程id是" << std::this_thread::get_id() << endl;
A a(10);
int num = 100;
//第一个参数是对象函数指针,第二个参数对象,其后参数为函数所需参数
thread th(&A::thread_work, a,num);
th.join();
cout << "主线程的num=" << num << endl;
cout << "主线程的a.num=" << a.num << endl;
cout << "主线程执行完毕" << endl;
return 0;
}
#include
#include
using namespace std;
int main() {
auto test = [] {
cout << "线程开始执行" << endl;
for (int i = 0; i < 10; ++i);
cout << "线程执行完毕" << endl;
};
thread th(test);
th.join();
cout << "主线程执行完毕" << endl;
return 0;
}
每个 std::thread 对象都有一个 ID,使用下面的函数可以获取:
std::thread::get_id()
如果 std::thread 对象没有和任何对象关联,则 get_id() 函数会返回默认构造的 std::thread::id 对象,即“非线程”。
获取当前线程的 ID:
std::this_thread::get_id()
用法示例
#include
#include
void thread_function()
{
std::cout<<"Inside Thread :: ID = "<<std::this_thread::get_id()<<std::endl;
}
int main()
{
std::thread threadObj1(thread_function);
std::thread threadObj2(thread_function);
if(threadObj1.get_id() != threadObj2.get_id())
std::cout<<"Both Threads have different IDs"<<std::endl;
std::cout<<"From Main Thread :: ID of Thread 1 = "<<threadObj1.get_id()<<std::endl;
std::cout<<"From Main Thread :: ID of Thread 2 = "<<threadObj2.get_id()<<std::endl;
threadObj1.join();
threadObj2.join();
return 0;
}
#include
#include
using namespace std;
class A {
public:
int num;
//类型转换构造函数,可以把一个int转换成一个类A对象
A(int num) :num(num) {
cout << "构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl;
}
A(const A& a) :num(a.num) {
cout << "拷贝构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl;
}
~A() {
cout << "析构函数执行" << this << "threadid=" << std::this_thread::get_id() << endl;
}
};
void test(const A& a) {
cout << "线程test的参数地址是" << &a << "threadid=" << std::this_thread::get_id() << endl;
}
int main() {
cout << "主线程id是" << std::this_thread::get_id() << endl;
int num = 2;
thread th(test, num);
th.join();
cout << "主线程执行完毕" << endl;
return 0;
}
C++11 并发编程系列(一):多线程初探(thread)
[c++11]多线程编程(二)——理解线程类的构造函数
C++11多线程编程
c++11线程基本用法
[c++11]多线程编程(一)——初识