• C++多线程 join、detach、joinable




    join()

    • 进程是资源分配的最小单位
    • 线程共享进程的栈空间,但是每个线程拥有独立的栈
    • 主程序调用join目的是
      等待子线程退出,回收他的资源
      如果子线程已经推出,join、立即执行
      如果没有退出,join阻塞,直到子线程退出
      注意
      不能同时对一个线程使用join和detach
    • 格式
    thread my(show);
    my.join();
    
    • 1
    • 2

    detach()

    • 主程序调用detach目的:
      分离子线程,
      子线程退出时系统才自动回收资源
    • 注意
      不能同时对一个线程使用join和detach
    • 另外可以通过延迟[sleep]主程序return
      来查看detach执行情况
      如果不sleep可能看不到结果,
      但是不影响程序后台执行
      在这里插入图片描述
      在这里插入图片描述

    joinable()

    bool类型函数,它会表示当前的线程是否是可执行线程(能被join或者detach)
    1 -> 表示可以join或者detach
    0 ->表示已经可以join或者detach不可再重复回收资源
    在这里插入图片描述
    在这里插入图片描述

    this_thread全局函数

    this_thread::get_id()

    获取线程ID

    void fun(int ret, const string & str) {
    		cout << "我是第" << ret << "个成员,我的线程ID是: ";
    		cout << this_thread::get_id() << endl;
    }
    int main() {
    		thread my(fun, 10, "哈啊哈哈");
    		my.join();
    		thread my1(fun, 11, "wssdasda哈");
    		cout << "我是主线程,我操作的my1的子线程ID是:" << my1.get_id() << endl;
    		my1.join();
    		cout << "我是主线程,我操作的my1的子线程ID是:" << my1.get_id();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    第二次my1的线程id是0是因为他已经被回收

    在这里插入图片描述

    this_thread::sleep_for()

    线程休眠
    时间参数是休眠的具体时间

    //方法1
    chrono::milliseconds dura(1000);
    this_thread::sleep_for(dura);
    //方法2
    this_thread::sleep_for(chrono::milliseconds(100));
    	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    this_thread::sleep_until()

    时间参数是时间点【略】

    this_thread::yield()

    让线程放出自己抢到的时间片

    swap(不属于this_thread命名空间下)

    格式(二者选其一)
    swap(a,b);
    a.swap(b);

    在这里插入图片描述

    移动构造函数拷贝进程[不在this::thread]

    thread类的拷贝函数被删除
    所以用移动构造函数赋值
    另外赋值的已经转移,原来的线程付给了新的
    原来的废弃,新的接管
    如下图my不可再用,my1接管my
    使用move可以转为右值在这里插入图片描述
    在这里插入图片描述

    创建线程

    void test01();//函数作为卡调用对象传入
    void test02();//类作为可调用对象传入
    void test03();//lambda匿名函数

    普通函数创建线程

    void fun(int ret, const string& str) {
    	cout << "我是第" << ret << "个成员,我要说话:“" << str << "”" << endl;
    }
    int main() {
    	thread my(fun, 10, "哈啊哈哈");
    	my.join();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    lambda匿名函数类创建线程

    		auto f = [](int ret, const string& str) {
    		cout << "我是第" << ret << "个成员,我要说话:“" << str << "”" << endl;
    	};
    	thread my(f, 10, "哈啊哈哈");
    	my.join();
    	//注意default和传入值
    	thread my1([](int ret = 10, const string &str = "啊啊啊啊啊") {
    		cout << "我是第" << ret << "个成员,我要说话:“" << str << "”" << endl;
    		},50,"aaaaaa");
    	my1.join();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    类创建线程

    仿函数创建线程

    切记仿函数无参不创建匿名类对象【原因不详】

    在这里插入图片描述

    #include 
    #include 
    using namespace std;
    class stu {
    public:
    
    	void operator()(int ret, const string& str);
    };
    void stu::operator()(int ret, const string& str)
    {
    	cout << "我是第" << ret << "个成员,我要说话:“" << str << "”" << endl;
    }
    int main() {
    	stu s1;
    	thread name(s1, 20, "哈哈哈");
    	name.join();
    	//注意用仿函数类名对象,必须带参数,如果无参只能用上面的不能用stu()
    	thread name1(stu(), 30, "aaa");
    	name1.join();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述

    类的静态成员函数创建线程

    #include 
    #include 
    using namespace std;
    class stu {
    public:
    
    	static void fun(int ret, const string& str);
    };
     void stu::fun(int ret, const string& str)//类成员函数类外写
    {
    	cout << "我是第" << ret << "个成员,我要说话:“" << str << "”" << endl;
    }
    int main() {
    	thread name(stu::fun, 3, "hello");
    	name.join();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    类的普通成员函数【注意】

    ①、必须先创建对象
    ②、对象的生命周期比子线程长
    ③、注意传进去的是指针
    thread name(&stu::fun,&s1, 3, “hello”);

    #include 
    #include 
    using namespace std;
    class stu {
    public:
    	void fun(int ret, const string& str);
    };
     void stu::fun(int ret, const string& str)//类成员函数类外写
    {
    	cout << "我是第" << ret << "个成员,我要说话:“" << str << "”" << endl;
    }
    int main() {
    	stu s1;
    	thread name(&stu::fun,&s1, 3, "hello");
    	name.join();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    【创建线程总结之万变不离其宗】

    普通函数lambdastatic类成员函数

    thread thread_name(方法,参数1,参数2 , ... ,参数n)
    
    • 1

    仿函数类

    thread thread_name(类对象,参数1,参数2 , ... ,参数n)
    
    • 1

    注意如果是匿名类对象(stu())则必须有参数,无参的可使用匿名类对象

    普通成员函数

    thread thread_name(方法,类对象(不可匿名),参数1,参数2 , ... ,参数n)
    
    • 1

    相关代码

    #include 
    using namespace std;
    //thread是一个类
    #include //线程头文件linux和win
    
    //show函数
    void show(){
    cout<<"我是show函数子线程1"<<endl;
    cout<<"我是show函数子线程2"<<endl;
    cout<<"我是show函数子线程3"<<endl;
    cout<<"我是show函数子线程4"<<endl;
    cout<<"我是show函数子线程5"<<endl;
    }
    //stu类
    class stu
    {
    public:
    void operator()(){//方函数
    cout<<"我是class类子线程1"<<endl;
    cout<<"我是class类子线程2"<<endl;
    cout<<"我是class类子线程3"<<endl;
    cout<<"我是class类子线程4"<<endl;
    cout<<"我是class类子线程5"<<endl;
    }
    };
    void test01();//函数作为卡调用对象传入
    void test02();//类作为可调用对象传入
    void test03();//lambda匿名函数
    int main(){
    //test01();
    //test02();
    test03();
    cout<<"我是主线程1"<<endl;
    cout<<"我是主线程2"<<endl;
    
    return 0;
    
    }
    void test03(){
    //()无参数可以省略小括号
    thread my_xc([](){
    cout<<"我是lambda匿名函数子线程1"<<endl;
    cout<<"我是lambda匿名函数子线程2"<<endl;
    cout<<"我是lambda匿名函数子线程3"<<endl;
    cout<<"我是lambda匿名函数子线程4"<<endl;
    cout<<"我是lambda匿名函数子线程5"<<endl;
    });
    my_xc.join();
    }
    void test02(){
    stu s1;
    thread my_xc(s1);
    //my_xc.join();
    //detach个走个的,输出顺序会混乱
    my_xc.detach();//注意join后不可以detach,二者只能有一个
    
    }
    void test01(){
    //子线程也从函数开始执行(初始函数)(初始函数结束则线程结束)
    thread my_xc(show);//show[函数是一个可调用对象
    //join-->阻塞主线程,让主线程等待子线程执行完毕,会合后一起走
    //注释掉join或放到return 0 后面
    //报错terminate called without an active exception
    //my_xc.join();//主线程被堵住子线程依然可以走
    
    //joinable可判断是否join、detach过
    cout<<"是否可以join或detach:"<<my_xc.joinable()<<endl;
    
    //主线程从mian函数开始(一般主线程结束子线程就会被强行结束[detach例外])
    //主线程和子线程没关系可以用detach【注意join之后不可再detach】
    my_xc.detach();//【守护线程】(子线程在后台运行)【但是不建议用detach】
    cout<<"是否可以join或detach:"<<my_xc.joinable()<<endl;
    }
    
    
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    使用detach格外小心

    传递int类型时不建议用引用(detach)
    如果传递类对象要进行转换,不要让编译器进行隐式转换
    【使用临时对象可以确保线程参数再mian函数结束之前就被构造出来】
    thread my(show1,i,string(buff));
    thread my(show1,i,mydelfclass(buff));

    #include 
    using namespace std;
    //thread是一个类
    #include //线程头文件linux和win
    
    //类
    class stu{
    public:
    int m;
    stu(int m):m(m){
    cout<<"stu的构造函数id:"<<this_thread::get_id()<<endl;
    }
    stu(const stu &s){
    cout<<"stu的拷贝函数"<<endl;
    }
    ~stu(){
    cout<<"stu的西沟函数"<<endl;
    }
    };
    
    //show函数
    void show(const int &i ,const string &mybuff){
    cout<<i<<endl;
    cout<<mybuff<<endl;
    }
    //class_show
    void show_class(const int &i ,const stu &my_stu){
    cout<<&my_stu<<endl;
    }
    
    
    void test01();
    void test02();
    void test03();
    int main(){
    //test01();
    test02();
    
    cout<<"我是主线程1:"<<this_thread::get_id()<<endl;
    cout<<"我是主线程2:"<<this_thread::get_id()<<endl;
    
    return 0;
    
    }
    
    void test02(){
    int i = 1;
    int arr = 12;
    
    thread my(show_class,i,stu(arr));
    
    my.detach();//但是detach会存在问题【不推荐引用,但是指针一定不可以】
    }
    void test01(){
    int i = 1;
    int &arr = i;
    char buff[] = "我是子线程";
    //string(buff)对这个对象进行转【临时的string对象】
    thread my(show,i,string(buff));
    //my.join();//show用的是i是thread的拷贝的新地质
    my.detach();//但是detach会存在问题【不推荐引用,但是指针一定不可以】
    }
    
    
    
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    在这里插入图片描述

    趣味练习 之 线程排序

    sleep排序法

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    using namespace std;
    vector <double> res;
    mutex flag;
    void show(const double ret) {
    	Sleep(ret*1000);//排序的数字越细,*的数字要越大
    	flag.lock();
    	res.push_back(ret);
    	cout << "我是" << ret << "我排序完毕" << endl;
    	//cout << "我的线程ID:" << this_thread::get_id() << endl;
    	flag.unlock();
    }
    
    int main() {
    	vector <double>ret = { 10,8,20,11,21,21.001};
    	thread n1(show, ret[0]);
    	thread n2(show, ret[1]);
    	thread n3(show, ret[2]);
    	thread n4(show, ret[3]);
    	thread n5(show, ret[4]);
    	thread n6(show, ret[5]);
    	n1.join();
    	n2.join();
    	n3.join();
    	n4.join();
    	n5.join();
    	n6.join();
    	for (auto a : res)
    		cout << a << endl;
    }
    
    • 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 
    #include 
    #include 
    #include 
    #include 
    #include 
    using namespace std;
    vector <double> res;
    mutex flag;
    void show(const double ret) {
    	flag.lock();
    	res.push_back(ret);
    	cout << "我是:" << ret << endl;
    	flag.unlock();
    }
    
    int main() {
    	bool flag = 0;
    	a:
    	vector <double>ret = { 10,8,20,11,21,21.001};
    	thread n1(show, ret[0]);
    	thread n2(show, ret[1]);
    	thread n3(show, ret[2]);
    	thread n4(show, ret[3]);
    	thread n5(show, ret[4]);
    	thread n6(show, ret[5]);
    	n1.join();
    	n2.join();
    	n3.join();
    	n4.join();
    	n5.join();
    	n6.join();
    	for (int i = 0; i < res.size()-1; i++)
    	{
    		if (res[i] <= res[i + 1])
    			flag = 1;
    		else {
    			flag = 0;
    			res.clear();
    			cout << "排序不对,再进行新一轮排序" << endl;
    			goto a;
    		}
    	}
    //	if (flag == 1)
    	cout << "排序完毕"<<endl;
    		for (auto a : res)
    			cout << a << endl;
    }
    
    
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    在这里插入图片描述

  • 相关阅读:
    NFTFi终极指南
    LeetCode之二叉树
    1. 梯度下降法
    三、kotlin的类和对象(二)
    线程与进程
    【华为OD机试真题 python】 城市聚集度【2022 Q4 | 200分】
    unity 渲染环境设置
    【数据挖掘】滴滴公司数据挖掘工程师笔试题
    基于rancher安装部署k8s
    【大虾送书第十期】从不了解用户画像,到用画像数据赋能业务看这一本书就够了
  • 原文地址:https://blog.csdn.net/weixin_45646601/article/details/127709058