系统的学习一下C++标准的多线程----线程管理

目录

目录

基本线程管理

启动线程

等待线程完成

在后台运行线程

传递参数给线程函数

传一个参数

传值

传引用

传指针

传多个参数

转移线程的所有权

在运行时选择线程的数量

结果

标识线程



基本线程管理

启动线程

启动线程,我这里总结了三种方法,第一种是最简单的使用匿名函数的方式,第二种使用小括号,第三种是使用大括号。

线程在创建的时候就已经启动了,不用显示启动。这里跟python不一样,python的线程我记得还得显示的启动才可以。

看代码:

#include <iostream>
#include <thread>

using namespace std;

void func2()
{
	cout << " -- thread-2 cout --" << endl;
}
void func3()
{
	cout << " -- thread-3 cout --" << endl;
}
int main()
{
	std::thread t1([]() {
		cout << " -- thread-1 cout --" << endl;	
	});
	std::thread t2(func2);
	std::thread t3{func3};
	cout << "-- main cout  --" << endl;
	if (t1.joinable())
	{
		t1.join();
	}
	
	t2.join();
	t3.join();
	
	return 0;
}

结果:

等待线程完成

等待线程完成就是调用jion函数。

	t1.join();
	t2.join();
	t3.join();

如果没有这三个函数,代码有可能会崩。

 因为有可能新建的线程还没有运行完,但是主函数已经运行完了,当线程函数运行完要回到主函数中,发现主函数运行完了,内存中没有主函数了,所以就崩了。有了join函数,主函数会阻塞到线程函数执行完返回主函数,主函数才会继续运行。如果不想线程函数再回到主函数,也可以把线程函数分离出去,这就下面说的在后台运行线程。或者称为守护线程。

	if (t1.joinable())
	{
		t1.join();
	}

joinable()函数是检查t1是否还需要等待,用来判断是否能够调用join()或者detach(),可以返回true,不可以返回false.
注意同一个线程不能同时调用jion()和detach()--分离线程的。

因为有时候创建完线程后,主线程取干其他的了,当再调用join函数的时候要是子线程运行的快,或者代码少,子线程其实这时候都运行完了,就不需要join了,所以用joinable函数判断一下,避免子线程运行完还在join子线程。

在后台运行线程

#include <iostream>
#include <thread>

using namespace std;

void func2()
{
	cout << " -- thread-2 cout --" << endl;
}
void func3()
{
	cout << " -- thread-3 cout --" << endl;
}
int main()
{
	std::thread t1([]() {
		cout << " -- thread-1 cout --" << endl;	
	});
	std::thread t2(func2);
	std::thread t3{func3};
	cout << "-- main cout  --" << endl;
	if (t1.joinable())
	{
		t1.join();
	}
	
	t2.detach();
	if (t3.joinable())
	{
		t3.detach();
	}
	
	return 0;
}

这段代码其实t2和t3是分离了的线程,意思是子线程运行完不回归主线程了,即使主线程运行完了,子线程依然还在运行也可以,也不会出现上面的错误情况。这种情况一般称子线程为守护线程。当然子线程运行完了也不会通知主线程。相当于两个线程互不干扰。

传递参数给线程函数

总结一下给线程传参的例子:

总代码:分析例子的时候一个一个一个分析:

#include <iostream>
#include <thread>
#include <string>

using namespace std;

void func2(int i)
{
	std::cout << " -- thread-2 i = " << i << " cout --" << endl;
}
void func3(string& str)
{
	std::cout << " -- thread-3 str = " << str << " cout --" << endl;
}
void func4(int i ,string* sstr,string& str)
{
	std::cout << " -- thread-3 i = " << i << " sstr = " << *sstr << " str = " << str << " cout --" << endl;
}
int main()
{
	int a = 10;
	string str = "qwe";
	string* pstr = &str;
	const string* cpstr = &str;
	string& ppstr = str;
	const string& cssstr = "zxc";
	std::thread t1([](const string* sstr) {
		std::cout << " -- thread-1 sstr = " << *sstr << " cout --" << endl;
	}, cpstr);
	std::thread t2(func2,a);
	std::thread t3{ func3,ref(str) };
	std::thread t4([](const string& ssstr) {
		std::cout << " -- thread-4 ssstr = " << ssstr << " cout --" << endl;
	}, cref(cssstr));
	std::thread t5{ func4,a,pstr,ref(ppstr) };

	std::cout << "-- main cout  --" << endl;
	if (t1.joinable())
	{
		t1.join();
	}
	if (t2.joinable())
	{
		t2.detach();
	}
	
	if (t3.joinable())
	{
		t3.detach();
	}
	if (t4.joinable())
	{
		t4.join();
	}
	if (t5.joinable())
	{
		t5.join();
	}
	return 0;
}

上面的代码是我运行过的,没问题,但是比较乱,下面一个类型一个类型的分析。

传一个参数

传值

t1是一个匿名函数传值的demo ,t2是一个普通函数传值的demo

#include <iostream>
#include <thread>
#include <string>
using namespace std;
void func(int i)
{
	std::cout << " -- thread-2 i = " << i << " cout --" << endl;
}
int main()
{
	int a = 10;
	std::thread t1([](int a) {
		std::cout << " -- thread-1 a = " << a << " cout --" << endl;
	}, a);
	std::thread t2(func, a);
	std::cout << "-- main cout  --" << endl;
	if (t1.joinable())
	{
		t1.join();
	}
	if (t2.joinable())
	{
		t2.detach();
	}
	return 0;
}


传引用

强调一点,线程传引用需要ref()这个是引用或者cref()这个是const引用函数。如果传引用不用这两个函数回出错。因为thread在传递参数时,是以右值传递的。

很明显的右值引用,那么我们该如何传递一个左值呢?std::refstd::cref很好地解决了这个问题。
std::ref 可以包装按引用传递的值。
std::cref 可以包装按const引用传递的值。

#include <iostream>
#include <thread>
#include <string>
using namespace std;
void func(string& str)
{
	std::cout << " -- thread-2 str = " << str << " cout --" << endl;
}
int main()
{
	string a = "qwe";
	string& str = a;
	std::thread t1([](string& str) {
		std::cout << " -- thread-1 a = " << str << " cout --" << endl;
	}, ref(str));
	std::thread t2(func, ref(str));
	std::cout << "-- main cout  --" << endl;
	if (t1.joinable())
	{
		t1.join();
	}
	if (t2.joinable())
	{
		t2.detach();
	}
	return 0;
}

传指针

#include <iostream>
#include <thread>
#include <string>
using namespace std;
void func(string* str)
{
	std::cout << " -- thread-2 str = " << *str << " cout --" << endl;
}
int main()
{
	string a = "qwe";
	string* str = &a;
	std::thread t1([](string* str) {
		std::cout << " -- thread-1 a = " << *str << " cout --" << endl;
	}, str);
	std::thread t2(func, str);
	std::cout << "-- main cout  --" << endl;
	if (t1.joinable())
	{
		t1.join();
	}
	if (t2.joinable())
	{
		t2.detach();
	}
	return 0;
}


传多个参数

#include <iostream>
#include <thread>
#include <string>
using namespace std;
void func(int i, string* pstr, string& sstr)
{
	std::cout << " -- thread-2 i = " << i << " pstr = " << *pstr << " sstr = " << sstr << " cout --" << endl;
}
int main()
{
	int i = 10;
	string a = "qwe";
	string* pstr = &a;
	string& sstr = a;
	std::thread t1([](int i, string* pstr, string& sstr) {
		std::cout << " -- thread-1 i = " << i << " pstr = " << *pstr << " sstr = " << sstr << " cout --" << endl;
	}, i, pstr, ref(sstr));
	std::thread t2(func, i, pstr, ref(sstr));
	std::cout << "-- main cout  --" << endl;
	if (t1.joinable())
	{
		t1.join();
	}
	if (t2.joinable())
	{
		t2.detach();
	}
	return 0;
}

转移线程的所有权

假设你想要编写一个函数,它创建一个后台运行的线程,但是向调用函数回传新线程的所有权,而非等待其完成,又或者你想要反过来做,创建一个线程,并将所有权传递到要等待它完成的函数,在任意一种情况下,你都需要将所有权从一个地方转移到另外一个地方。

#include <iostream>
#include <thread>
#include <string>

using namespace std;

void func()
{
	cout << "thread1" << endl;
}
int main()
{
	thread t1(func);
	thread t2 = move(t1);
	if (t2.joinable())
	{
		t2.join();
	}
	if (t1.joinable())
	{
		t1.join();
	}
	return 0;
}

运行结果,t2拥有t1线程的所有权,而t1则指向的nullptr所以,t2.joinable()函数返回的是true,而t1.joinable()函数返回的是false,不执行t1.join()函数。因为某种意义上已经不存在t1线程了。

在运行时选择线程的数量

要运行的线程数小于硬件线程数量。超过及就超额订阅。因为上下文切切换将占用线程,会降低性能。

一般你启动的线程要比硬件线程少一个,因为已经有一个主线程了。

我现在用的CPU是4核4线程的。所以返回的值是4.

#include <iostream>
#include <thread>
#include <string>

using namespace std;

void func()
{
	cout << "thread1" << endl;
}
int main()
{
	thread t1(func);
	thread t2 = move(t1);
	if (t2.joinable())
	{
		t2.join();
	}
	if (t1.joinable())
	{
		t1.join();
	}
	int a = std::thread::hardware_concurrency();
	cout << "thread num = " << a << endl;
	return 0;
}

结果

标识线程

线程标识符是std::thread::id类型的,并且有两种获取方式。其一,线程的标识符可以通过std::thread对象中通过get_id()成员函数来获得。如果std::thread对象没有相关联的执行线程,对get_id()的调用返回一个默认构造函的std::thread::id对象,表示没有线程。

另外当前线程的标识符,可以通过std::this_thread::get_id()获得,这也定义在<thread>头文件中。

std::thread::id类型的对象可以自由地复制和比较,否则他们作为标识符就没什么大用处。如果两个std::thread::id类型的对象相等,则它们代表着同一个线程,或者两者都具有“没有线程”的值。

#include <iostream>
#include <thread>
#include <string>

using namespace std;

void func()
{
	cout << "thread1" << endl;
}
int main()
{
	thread t1(func);
	thread t2 = move(t1);
	cout << "get_t1_thread_id = " << t1.get_id() << endl;
	cout << "get_t2_thread_id = " <<  t2.get_id() << endl;
	if (t2.joinable())
	{
		t2.join();
	}
	if (t1.joinable())
	{
		t1.join();
	}	
	return 0;
}

#include <iostream>
#include <thread>
#include <string>

using namespace std;

void func()
{
	cout << "thread1 id = " << std::this_thread::get_id() << endl;
}
int main()
{
	thread t1(func);
	thread t2 = move(t1);
	cout << "get_t1_thread_id = " << t1.get_id() << endl;
	cout << "get_t2_thread_id = " <<  t2.get_id() << endl;

	cout << "2_get_t1_thread_id = " << std::this_thread::get_id() << endl;	
	if (t2.joinable())
	{
		t2.join();
	}
	if (t1.joinable())
	{
		t1.join();
	}	
	return 0;
}

 总结:

td::this_thread::get_id() 

是在函数体内用的得到的是当前线程的id,在哪里用就是求当前线程的id,不能求别的线程id。

cout << "get_t2_thread_id = " <<  t2.get_id() << endl;

 这个是求t2线程的id可以在主线程求子线程的id。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

波雅_汉库克

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值