c++ 多线程 Thread

并发:同一时段
并行:同一时刻
每一个进程都必有一个主线程,主线程是用来调用main函数的。
同一电脑进程间通信方式:管道,文件,消息队列,共享内存(速度最快)
不同电脑进程间通信方式:socket

创建thread对象的方法
#include
1.通过函数创建:

void print(){
	cout<<"this is print"<<endl
}

thread mythread(print);

2.重载()运算符的类的对象

class myclass{
	public :
	void operator()(){
		cout<<"this is operator"<<end;
	}
};

myclass mc;
thread mythread(mc); 
或者 thread mythread((myclass())); 

用对象创建线程时,线程中的对象是一个拷贝的新的对象与原对象值相同但没有关联,若原对象被析构,新对象依然可以使用。
3.lambda表达式

auto mylambda=[]{
	cout<<"this is lambda"<<endl;
}
thread mythread(mylambda);

thread函数:

(1)thread() noexcept;
(2)template <class Fn, class... Args>explicit thread (Fn&& fn, Args&&... args);
(3)thread (const thread&) = delete;
(4)thread (thread&& x) noexcept;
(1). 默认构造函数,创建一个空的 thread 执行对象。
(2). 初始化构造函数,创建一个 thread对象,该 thread对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
(3). 拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
(4). move 构造函数,move 构造函数,调用成功之后 x 不代表任何 thread 执行对象。

void f1(int n)
{
    for (int i = 0; i < 5; ++i) {
        cout << "Thread " << n << " executing\n";
    }
}
void f2(int& n)
{
    for (int i = 0; i < 5; ++i) {
        cout << "Thread 2 executing\n";
        ++n; 
    }
}
 int n = 0;
 thread t1; // t1 is not a thread
 thread t2(f1, n + 1); // pass by value
 thread t3(f2, std::ref(n)); // pass by reference
 thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a 
join();      加入阻塞,当一个父线程调用了一个子线程,当子线程join时,父线程会等到子线程执行完毕再执行
detach();       表示该线程在后台允许,失去和父线程的关系,它们的执行关系随机,但是当主线程执行完毕时,所有线程都会结束运行。 detach和join不能并存
joinable();    返回一个bool变量,表示当前线程是否能够join和detach,当我们在join或者detach之前应该调用joinable函数判断当前线程是否能够join和detach。
get_id();  返回int变量,表示该线程的id

void myprint() {
	cout << "this is a print1" << endl;
	cout << "this is a print2" << endl;
	cout << "this is a print3" << endl;
	cout << "this is a print4" << endl;
}
int main() {
	thread ob(myprint);
	if (ob.joinable()) {
		ob.join(); 
		//ob.detach;
		cout<<"thread id:   "<<id<<endl;
		
	}
	cout << "is over1" << endl;
	cout << "is over2" << endl;
	cout << "is over3" << endl;
	cout << "is over4" << endl;
	system("pause");
	return 0;
}
第一幅图是调用join的运行结果,第一幅图和第三幅是调用detach的运行结果,发现第一幅图和第三幅的打印顺序并不一样。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

一些陷阱:

void  print(consr int &i,char *carr){
	cout<<i<<endl;
	cout<<carr<<endl;
}
void  print(consr int &i,const string &carr){  //修改一下
	cout<<i<<endl;
	cout<<carr<<endl;
}
int mvar=1;
int &mmvar=mvar;
char mybuf[]="this is a test";
thread mythread(print,mmvar,mybuf);  
mythread.detach();  //当使用detach时,mmvar和i的指向同但不是一对象,当mmvar被释放时,i仍然能够使用,但carr和mybuf是一样的,mybuf被释放后,carr就不能用了,这样调用detach时会出现错误。修改之后,变为string的carr和mmvar的地质不一样说明他们不是一个对象,但是因为主线程和子线程的执行顺序不确定,若主线程先执行并释放那么carr的构造仍然是错误的。

改写为:
thread mythread(print,mmvar,string(mybuf));  //string(mubuf)创建了一个临时对象,这是函数就是正确的了。任意类型的对象都可以这样。但是函数参数中仍然不能存在指针。

传递类对象作为函数参数
class A{
	private:
		int m_i;
	public:
		A(int i):m_i(i){}
		~A(){}
		void A_print(int num){
			cout<<num<<endl;
		}
}
A a(10);
void  print(A &c){ 
	cout<<c.m_i<endl;
}
thread mythread(print,std::ref(a));  
智能指针作为函数参数
void  print(unique_ptr<int> p){ 
}
unique_ptr<int> q(int int(100));
thread mythread(print,std::move(q))
成员函数指针做参数
thread mythread(&A::A_print,a,15)

多个线程:

	vector<thread> mythread;
	for (int i = 0; i < 10;i++) {
		mythread.push_back(thread(myprint,i));
	}
	auto it = mythread.begin();
	while(it!=mythread.end()) {
		it->join();
		it++;
	}
只读数据时:
vector<int> g_v = { 1,2,3 };
void myprint() {
	cout << std::this_thread::get_id()<<g_v[0] << g_v[1] << g_v[2] << endl;
}
vector<thread> mythread;
	for (int i = 0; i < 10;i++) {
		mythread.push_back(thread(myprint));
	}
	auto it = mythread.begin();
	while(it!=mythread.end()) {
		it->join();
		it++;
	}

共享数据保护:
使用mutex给共享数据加锁

class A {
public:
	void getMessage() {
		for (int i = 0; i < 10000;i++) {
			cout << "收到一个信息 " <<i<< endl;
			std::lock(mymutex, mymutex2);
			//mymutex.lock();
			//mymutex2.lock();
			mylist.push_back(i);
			mymutex.unlock();
			mymutex2.unlock();
		}
	}
	bool outInf(int &command) {
		//std::lock_guard<mutex> mylockguard(mymutex); //构造函数阶段lock(),析构函数阶段unlock()	
		//std::lock() 一次能够锁住2个或者2个以上的互斥量,至少2个锁,一次性锁住所选的互斥量,若有一个互斥量已经被锁,则
		//释放掉自己所锁住的互斥量然后等待所选的互斥量都未被加锁
		//mymutex.lock();
	    //mymutex2.lock();
		std::lock(mymutex,mymutex2);
		//存在std::adopt_lock时,lock_guard不会在构造函数中调用lock()
		std::lock_guard<mutex> mylockguard(mymutex,std::adopt_lock);
		std::lock_guard<mutex> mylockguard(mymutex2,std::adopt_lock);
		if (!mylist.empty()) {
			command = mylist.front();
			cout << "输出信息 " << command << endl;
			mylist.pop_front();
			//mymutex.unlock();
			//mymutex2.unlock();
			return true;
		}
		else {
			//mymutex.unlock();
		    //mymutex2.unlock();
			return false;
		}
	}
	void outMessage() {
		int c = 1;
		for (int i = 0; i < 10000; i++) {			
			bool res = outInf(c);
			if (res == true) {
				cout << "信息为:" << c << endl;
			}
			else {
				cout << "没有信息:" << endl;
			}
		}
		
	}
private:
	list<int> mylist;
	mutex mymutex;   //互斥量
	mutex mymutex2;  //互斥量
};

unique_lock比lock_guard灵活
std::adopt_lock():标志位,表示这个互斥量已经被lock了,若互斥量没有被加锁,则不能用这个参数
mutex mymutex;
mymutex.lock()
std::unique_lock <mutex> mylockguard(mymutex, std::adopt_lock);
std::try_to_lock():会尝试用mutex的lock()去锁定这个锁,但如果没有锁定成功,也会立即返回,不会阻塞,
unique_lock <mutex> mylockguard(mymutex, std::try_to_lock);
注意自己不能提前lock,否则可能导致一个互斥量锁2次
std::defer_lock():创建一个没有加锁的mutex; 
unique_lock <mutex> mylockguard(mymutex, std::defer_lock);
mylockguard.lock();
unique_lock的成员函数
lock():
unique_lock <mutex> mylockguard(mymutex);
mylockguard.lock();
unlock();
mylockguard.unlock();
try_lock() 失败返回false,成功返回true
mylockguard.try_lock();
release() 返回它管理的互斥量对象指针,并释放所有权,也就是说unique_lock和这个互斥量再也没有关系
std::mutex *ptr=mylockguard.realease();
锁的粒度指锁所锁住的代码的数量
所有权的转移:
unique_lock <mutex> mylockguard2(std::move(mylockguard ));

单例模式:

mutex mymutex;
std::once_flag g_flag;
class SClass {
	static void CreatInstance() {
		std::chrono::milliseconds dura(10000);
		std::this_thread::sleep_for(dura);
		m_instance = new SClass();
		cout << "creat obj" << endl;
		static FreeObj cl;
	}
private:
	SClass() {}
private:
	static SClass *m_instance;
	
public:
	static SClass *getInstance() {
		//if (m_instance == NULL) {    //双重检查,提高效率,因为只是第一次初始化的代码需要加锁,
		//	  //大多数情况是不需要加锁,若只检查一次,意味着即使对象已经创建,仍然会做加锁操作,
		//	  //其它线程仍会等待,浪费时间
		//	std::unique_lock<mutex> myguard(mymutex);
		//	if (m_instance == NULL) {
		//		m_instance = new SClass();
		//		static FreeObj cl;
		//	}		
		//}
		std::call_once(g_flag,CreatInstance); //g_flag相当于一个互斥量,两个线程同时执行到这里时,
		                                      //其中一个线程会等到另一个线程执行完
		cout << "call once is over"<<endl;
		return m_instance;
	}
	class FreeObj {      //内部类,用来回收创建的对象所占的资源
	public:
		~FreeObj() {
			if (m_instance) {
				delete SClass::m_instance;
				SClass::m_instance = NULL;
			}
		}
	};
	void fun() {
		cout << "test" << endl;
	}
};
SClass * SClass::m_instance = NULL;
void mythread() {
	cout << "mythread线程开始" << endl;
	SClass *p = SClass::getInstance();
	p->fun();
}
//std::call_once():     有2个参数,第二个参数为一个函数名,保证函数a只能调用一次
//call_once()具备互斥量的能力,效率上比互斥量好
//call_once()需要与一个标记结合使用,std::once_flag,当已经调用了一次后,std::once_flag就被置为已调用
int main() {	
	//SClass *pa = SClass::getInstance();
	thread thread1(mythread);
	thread thread2(mythread);
	thread1.join();
	thread2.join();
	return 0;
}

未完待续

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值