多线程学习(一)

项目中用到了线程池,在此正式的总结一下,线程池说到底还是多线程,所以,将多线程的知识梳理一下。

基础

C++11中新增了线程库,用来支持多线程的开发。
相关头文件<condition_variable>
编译来说:
windows下visual studio2017已经支持
linux下gcc 4.8.1/g++ -std=c++11 xxx.cpp -lpthread

what is thread?

其实每个c++应用程序中都有一个默认的主线程,就是main函数。主线程用来规规矩矩的做自己的事情。
比如说我要做馒头。主线程就是一步一步来。烧火,和面,做馒头,煮水,蒸馒头。这其实说起来就是一个进程。但是,我们想早点吃上馒头怎么办?很简单,让人帮忙烧火煮水,你来和面做馒头。因为两个流程并不是相互影响的。这就是thread。

#include <iostream>
#include <thread>
#include <ctime>

using namespace std;

void shaohuozhushui()
{
	int i = 1;
	while (i < 1000)
	{
		cout << "I am a thread,I am shuishenhuoreing." << endl;
		i++;
	}

}

void zuomantou(int n)
{
	int j = 1;
	while (j < 1000)
	{
		cout << "T am also a thread,I will make " << n << " ge mantou." << endl;
		j++;
	}

}

int main()
{
	clock_t start, end;
	start = clock();
	cout << "make mantou begin!" << endl;
	//shaohuozhushui();
	//zuomantou(5);
	thread th1(shaohuozhushui);
	thread th2(zuomantou, 5);
	th1.join();
	th2.join();
	cout << "done!" << endl;
	end = clock();
	cout << "The runtime is " << (double)(end - start) / CLOCKS_PER_SEC << "s";
	return 0;
}

上面这个例子,我们让一个线程烧火煮水,一个做馒头。就是多线程。
注释里面的就是不用多线程,我们可以加个时间看一下,由于电脑环境不同,所以每次运行时间也不太相同。我简单截取两个对比下。

在这里插入图片描述在这里插入图片描述
前面的是主线程的,后面的是多线程的,由于例子过于简单,貌似没什么区别。但可以自己做一个看一看。过程中的结果太长所以我没有截取。做的过程就能看出来差异。
thread的创建,std::thread obj(callback)。新线程在创建后立即开始,里面可以附加一个回调,当线程启动时被执行。
回调函数的附加方法,可以看我上面的例子就是两种方法。
thread obj(xxx);
如果回调函数需要参数,则需要将参数也传递进去。thread obj(xxx,1,2);
当然,也可以通过类中的函数创建对象。例子见下。

#include <iostream>
#include <thread>

class MYClass {
public:
	MYClass() { }
	MYClass(const MYClass& obj) { }
	void function(int x) {
		std::cout << "I AM A THREAD IN MYCLASS " << x << std::endl;
	}
};

int main() {
	MYClass Obj;
	int x = 6;
	std::thread threadObj(&MYClass::function, &Obj, x);
	threadObj.join();
	return 0;
}

同时,每一个thread对象都有自己的id,可以获取到。
thread obj;
obj.get_id();//获得对象线程的ID
this_thread::get_id();//获得此线程的ID
例子如下:

#include <iostream>
#include <thread>

using namespace std;

void ID()
{
	cout << "I am a thread, my ID is " << this_thread::get_id() << endl;
}
int main() {
	thread th1(ID);
	thread th2(ID);
	if (th1.get_id() != th2.get_id())
	{
		cout << "th1's ID is" << th1.get_id() << endl;
		cout << "th2's ID is" << th2.get_id() << endl;
	}
	th1.join();
	th2.join();
	return 0;
}

在这里插入图片描述
结果如下,当然,大家的运行结果可能和这个不一样,我这也是为了演示作用,运行了好几次才捕捉到了这个输出。这是之后说的异步问题。

join和detach

我们在创建了一个线程之后就要回收这个线程,不能说让他一直烧火,烧的什么都没有了,让他一直做馒头,永远也吃不完了。
这就是join的作用,一个线程在创建之后,便可以通过此对象的join函数等待这个线程执行完毕回收。
在第一个例子里,主线程在两个线程join完毕之后,才继续执行之后的代码。
detach的不同之处在于,我创建了一个线程对象后,可以将他detach,执行操作后,此线程就与实际执行的线程脱离了关系,也可以这么理解,蒲公英的孩子可以到处乱飞,就是detach了。他执行完毕后会回收该线程资源。
但detach可能会有风险,那就是如果线程要操作某对象,但是此时主线程已经结束,析构了此对象时,就会出现异常。

#include <iostream>
#include <thread>

class MYClass {
public:
	MYClass() { }
	MYClass(const MYClass& obj) { }
	void function(int x) {
		std::cout << "I AM A THREAD IN MYCLASS " << x << std::endl;
	}
};

int main() {
	MYClass Obj;
	int x = 6;
	std::thread threadObj(&MYClass::function, &Obj, x);
	threadObj.detach();
	std::thread threadObj1(&MYClass::function, &Obj, x);
	threadObj1.detach();
	std::cout << "done" << std::endl;
	return 0;
}

原本应该是两个线程调用了Obj的成员函数,进行两次打印。但是结果如下。
在这里插入图片描述在这里插入图片描述
所以detach会有异常发生。
另外,join和detach都不能对一个对象重复使用。对一个对象join两次是会出错的。因此可以先判断joinable();

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值