主页
文章
分类
系列
标签
简历
【C++并发编程】线程管理
发布于: 2022-4-14   更新于: 2022-4-14   收录于: Cpp
文章字数: 285   阅读时间: 2 分钟   阅读量:

线程管理

启动线程

线程在 std::thread 对象创建(为线程指定任务)时启动

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
void myprint()
{
	cout<<"子线程"<<endl;
    //...
	cout<<"线程结束"<<endl;
	return ;
}
int main()
{
	std::thread mytobj(myprint);
	mytobj.join();
	return 1;
}

等待线程完成

调用.join()方法,可以要求主线程等待该线程完成后再退出 想象另一种情景,你需要主线程等待一段特定的时间、或者时不时检查以下线程是否仍在运行,则需要其他更为复杂的机制来完成,比如条件变量和期待。 .joinable()方法可以判断,当前线程对象是否可以加入(一个线程对象,不可重复加入)

特殊情况下的等待

相比于随时可以执行的.detach() , .join()在使用过程中则需要考量更多的问题,因为在调用.join()之前,运行的线程发生了异常,就会跳过.join() 两种方法

  1. 在函数异常处理中join
  2. 利用RAII

后台运行线程

使用detach()会让线程在后台运行,这就意味着主线程不能与之产生直接交互。也就是说,不会等待这个线程结束;如果线程分离,那么就不可能有 std::thread 对象能引用它,分离线程的确在后台运行,所以分离线程不能被加入。不过C++运行库保证,当线程退出时,相关资源的能够正确回收,后台线程的归属和控制C++运行库都会处理。

⚠️必须在 std::thread 对象销毁之前做出决定,否则你的程序将会终止( std::thread 的析构函数会调用 std::terminate() ,这时再去决定会触发相应异常)。

向线程函数传递参数

传递简单数据类型

std::thread 的入口函数中传递简单类型数据,如 int 等,本质上是值传递,可以放心的 detach,如下图:

如上如图,虽然是引用接受参数,但线程中参数的地址和主线程中参数地址却不一样,是拷贝了一个新的参数。

传递指针

std::thread 的入口函数中传递指针时,要小心使用 detach,如下图:

如上图,线程函数中参数首地址和形参首地址一样,要避免使用 detach()。

传递类对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <thread>
using namespace std;
class TA{
    void operator()()
    {
        cout<<"TA::operator()开始执行"<<endl;
        cout<<"TA::operator()结束执行"<<endl;
    }
};
int main()
{
    TA ta;
    thread mytobj(ta); //里不可以是临时对象TA(),否则编译无法通过
    mytobj.join();
    cout<<"main主函数结束执行"<<endl;
    return 0;
}

std::thread 的入口函数中传递类对象时,本质上也是传递的类对象的拷贝。

 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
36
37
38
39
40
41
#include "thread"
#include "iostream"
using namespace std;
class demoClass
{
public:
  demoClass(int var) :m_var(var)
  {
    cout << "构造函数执行,线程id:" << this_thread::get_id() << endl;
  }
  demoClass(const demoClass& demo) :m_var(demo.m_var)
  {
    cout << "拷贝构造函数执行,线程id:" << this_thread::get_id() << endl;
  }
  ~demoClass()
  {
    cout << "析构函数执行,线程id:" << this_thread::get_id() << endl;
  }
private:
  int m_var;
};

void functionToThread(const demoClass & demo)
{
  cout << "线程启动......" << endl;
  cout << "线程id:" << this_thread::get_id() << " demo 的地址 "<< &demo << endl;
  cout << "线程结束......" << endl;
}
int main()
{
  demoClass demo(20);
  cout << "线程id:" << this_thread::get_id() << " demo 的地址 " << &demo << endl;

  thread myThread(functionToThread, demo);

  myThread.join();

  cout << "主线程结束......" << endl;
  system("pause");
    return 0;
}

执行结果如下:虽然是引用接受参数,但主线程中参数对象的地址和传入子线程中对象地址不一样:

④.传递类对象的引用
使用 std::ref() 传递对象,则传递的时真引用,如下图:

⑤.传递临时对象
当 std::thread 的入口函数中参数通过隐式类型转换产生临时对象时,临时对象的构造是在子线程中执行的,如下图:

可以通过显示的调用构造函数来生成临时对象,来强制使类型转换在主线程中执行,如下图: