C++多线程基础

对应视频:c++11并发与多线程视频课程

线程创建、启动、结束

示例 thread()+join()

#include
#include
using namespace std;

//自己创建的线程需要从一个函数(初始函数)开始运行
void myPrint() {
	cout << "myPrint" << endl;
}

int main() {

	//程序运行起来,生成一个进程,该进程所属的主线程开始自动运行;当主线程从main()函数返回,则整个进程执行完毕
	//主线程从main()开始执行,那么我们自己创建的线程,也需要从一个函数开始运行(初始函数),一旦这个函数运行完毕,线程也结束运行
	//整个进程是否执行完毕的标志是:主线程是否执行完,如果主线程执行完毕了,就代表整个进程执行完毕了,此时如果其他子线程还没有执行完,也会被强行终止

	//thread是C++11的线程类,myprint是可调用对象
	thread mytobj(myPrint); //创建了线程,线程执行起点是myPrint();然后myPrint()开始执行
	
	//阻塞主线程(main函数),让主线程等待子线程执行完毕,然后让子线程和主线程汇合,然后主线程再往下走。
	mytobj.join();//如果不加这行可能导致子线程还没执行完主线程就执行完了,会产生报错

	cout << "main" << endl;

	//一个良好的程序应该是主线程等待子线程执行完毕后,自己才能最终退出
	return 0;
}

detach()

void myPrint() {
	cout << "1" << endl;
	cout << "2" << endl;
	cout << "3" << endl;
	cout << "4" << endl;
	cout << "5" << endl;
	cout << "6" << endl;
}

int main() {

	
	thread mytobj(myPrint); 
	
	//与主线程分离,主线程不用再等待子线程运行结束
	//一旦detach()以后,主线程就和子线程失去关联,此时子线程就会驻留在后台运行
	//这个子线程相当于被C++运行时库接管,当这个子线程执行完成后,由运行时库负责清理该线程的资源
	//detach()会使线程失去我们的控制
	//一旦调用了detach(),就不能再用join()
	mytobj.detach();

	cout << "main" << endl;

	return 0;
}

joinable()

void myPrint() {
	cout << "myPrint" << endl;
}

int main() {

	
	thread mytobj(myPrint);

	//joinable() 判断是否可以成功使用join()或者detach()
	cout << mytobj.joinable() << endl;
	
	mytobj.join();

	cout << mytobj.joinable() << endl;

	cout << "main" << endl;

	return 0;
}

用类对象创建线程

class TA {
public:
	void operator()() {
		cout << "TA" << endl;
	}
};

int main() {

	TA ta;
	thread mytobj(ta);//ta可调用对象
	mytobj.join();

	cout << "main" << endl;

	return 0;
}

lambda表达式创建线程

int main() {

	auto myLambda = [] {
		cout << "1" << endl;
	};
	
	thread mytobj(myLambda);
	mytobj.join();

	cout << "main" << endl;

	return 0;
}

用成员函数指针做线程函数

class A {
public:
	int m_i;
	A(int a) :m_i(a) {  }
	A(const A &a) :m_i(a.m_i) {  }
	~A() { }

	//线程执行入口
	void thread_work(int num) {
		cout << "thread_work" << endl;
	}
};

int main() {

	A myobj(10);
	thread mytobj(&A::thread_work, &myobj,15);//对象要用&或者std::ref(),不然会复制出一个新对象

	mytobj.join();

	return 0;
}

线程传参,detach()大坑

传递临时对象作为线程参数

void myprint(int i, int j) {
	cout << i << endl;
	cout << j << endl;
}

int main() {

	thread mytojb(myprint, 1, 2);//先是可调用对象,然后依次是函数参数
	mytojb.join();

	cout << "main" << endl;

	return 0;
}

detach()陷阱

陷阱1

指针指向的空间随着主线程的运行结束可能被销毁

void myprint(const int &i, char *pmybuf) {
	//这里i并不是真正的引用,实际是值传递
	//即使主线程detach了子线程,子线程中用i值仍然是安全的
	cout << i << endl;

	//指针绝对有问题,指向同样的内存空间
	cout << pmybuf << endl;
}

int main() {

	int mvar = 1;
	char mybuf[] = "this is a test!";

	thread mytojb(myprint, mvar, mybuf);//myprint的参数这里是值传递
	mytojb.detach();

	cout << "main" << endl;

	return 0;
}

陷阱2

改用string以后,可能存在隐式转换在主线程运行结束以后

void myprint(const int &i, const string &pmybuf) {
	cout << i << endl;
	cout << pmybuf.c_str() << endl;
}

int main() {

	int mvar = 1;
	char mybuf[] = "this is a test!";

	//存在问题:mubuf到底在什么时候隐式转成string
	//事实上存在 “mybuf都被回收了(main函数执行完了),系统才用mybuf去转string” 的情况
	thread mytojb(myprint, mvar, mybuf);
	
	mytojb.detach();

	cout << "main" << endl;

	return 0;
}

正确方法

void myprint(const int &i, const string &pmybuf) {
	cout << i << endl;
	cout << pmybuf.c_str() << endl;
}

int main() {

	int mvar = 1;
	char mybuf[] = "this is a test!";

    //在创建线程的同时构造临时对象的方法传递参数是可行的
	thread mytojb(myprint, mvar, string(mybuf));//直接将mybuf转成string对象,这样就可以保证在线程中用肯定有效的对象
	
	mytojb.detach();

	cout << "main" << endl;

	return 0;
}

获取线程id

线程id概念

  • id是个数字,每个线程(不管是主线程还是子线程)实际上都对应着一个数字,而且每个线程对应的这个数字都不一样
  • 线程id可以用C++标准库里的函数来获取。std::this_thread::get_id()来获取
class A {
public:
	int m_i;
	A(int a) :m_i(a) { cout << "构造函数执行" << this << " threadid=" << this_thread::get_id() << endl; }
	A(const A &a) :m_i(a.m_i) { cout << "拷贝构造函数执行" << this << " threadid=" << this_thread::get_id() << endl; }
	~A(){ cout << "析构函数执行" << this << " threadid=" << this_thread::get_id() << endl; }
};

void myprint(const A &a) {
	cout << "子线程a的参数地址是" << &a << " threadid=" << this_thread::get_id() << endl;
}

int main() {

	cout << "主线程id" << this_thread::get_id() << endl;

	int mvar = 1;
	thread mytobj(myprint, mvar);

	mytobj.join();

	cout << "main" << endl;

	return 0;
}

运行结果

主线程id11880
构造函数执行0198F9BC threadid=14772
子线程a的参数地址是0198F9BC threadid=14772
析构函数执行0198F9BC threadid=14772
main

从运行结果中可以看出,隐式类型转换是在子线程中构造A类对象

class A {
public:
	int m_i;
	A(int a) :m_i(a) { cout << "构造函数执行" << this << " threadid=" << this_thread::get_id() << endl; }
	A(const A &a) :m_i(a.m_i) { cout << "拷贝构造函数执行" << this << " threadid=" << this_thread::get_id() << endl; }
	~A(){ cout << "析构函数执行" << this << " threadid=" << this_thread::get_id() << endl; }
};

void myprint(const A &a) {
	cout << "子线程a的参数地址是" << &a << " threadid=" << this_thread::get_id() << endl;
}

int main() {

	cout << "主线程id" << this_thread::get_id() << endl;

	int mvar = 1;
	thread mytobj(myprint, A(mvar));

	mytobj.join();

	cout << "main" << endl;

	return 0;
}

运行结果

主线程id10568
构造函数执行00AFF968 threadid=10568
拷贝构造函数执行00D4FBC0 threadid=10568
子线程a的参数地址是00D4FBC0 threadid=5196
析构函数执行00AFF968 threadid=10568
析构函数执行00D4FBC0 threadid=5196
main

从运行结果中可以看出,用了临时对象后,所有的A类对象都在main()函数中就已经构建完毕了

线程中传真引用,线程中修改变量值,影响外面,std::ref()

void myprint(int &a) {
	cout << a << endl;
	a = 100;
}

int main() {

	int a = 1;
	thread mytobj(myprint, ref(a));

	mytobj.join();

	cout << a << endl;

	return 0;
}

参数传智能指针(unique_ptr)

void myprint(unique_ptr<int> p) {
	cout << *p << endl;
}

int main() {

	unique_ptr<int> p(new int(100));//独占式智能指针
	thread mytobj(myprint, move(p));//独占式智能指针不能拷贝,只能移动

	mytobj.join();

	return 0;
}

创建多个线程,数据共享

创建和等待多个线程

多个线程的执行顺序是乱的,跟操作系统内部对线程的运行调度机制有关。

//线程入口函数
void myprint(int inum) {
	cout << "myprint线程开始执行," << " 线程编号=" << inum << endl;

	cout << "myprint线程结束执行," << " 线程编号=" << inum << endl;
}

int main() {
	vector<thread> mythreads;//把thread对象放入到容器里管理,方便对线程的管理

	//创建10个线程,线程入口函数统一使用myprint
	for (int i = 0; i < 10; i++) {
		mythreads.push_back(thread(myprint, i));//创建并开始执行线程
	}
	for (auto iter = mythreads.begin(); iter != mythreads.end(); iter++) {
		iter->join();//等待10个线程都返回
	}

	cout << "main" << endl;
	return 0;
}

数据共享

只读数据

只读的数据,是安全稳定的,不需要特别的处理,直接读就可以

vector<int> g_v = { 1,2,3 };//共享数据(只读)

//线程入口函数
void myprint(int inum) {
	cout << "id为" << this_thread::get_id() << "的线程打印g_v的值" << g_v[0] << g_v[1] << g_v[2] << endl;
}

int main() {
	vector<thread> mythreads;//把thread对象放入到容器里管理,方便对线程的管理

	//创建10个线程,线程入口函数统一使用myprint
	for (int i = 0; i < 10; i++) {
		mythreads.push_back(thread(myprint, i));//创建并开始执行线程
	}
	for (auto iter = mythreads.begin(); iter != mythreads.end(); iter++) {
		iter->join();//等待10个线程都返回
	}

	cout << "main" << endl;
	return 0;
}

有读有写的数据

会出问题,会报错,需要使用锁(见下一章)

以下代码不使用锁,进程同时读写共享数据时会出问题

class A {
public:
	//收到的消息放入到一个容器中
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
			msgRecvQueue.push_back(i);//假设数字i就是收到的消息,放入消息队列中
		}
	}

	//把消息从容器中取出
	void outMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			if (!msgRecvQueue.empty()) {
				//消息队列不为空
				int msg = msgRecvQueue.front(); 
				msgRecvQueue.pop_front();
                
				//处理消息......
                
			}
			else {
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:
	list<int> msgRecvQueue;//专门用于存收到的消息的容器(消息队列)
};

int main() {

	A myobj;
	thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);//第二个参数加&保证在线程中用的是同一个对象
	thread myInMsgObj(&A::inMsgRecvQueue, &myobj);

	myOutMsgObj.join();
	myInMsgObj.join();

	cout << "main" << endl;
	return 0;
}

互斥量

互斥量(mutex)是个类对象,同一时刻多个线程都可以调用互斥量的成员函数lock()来尝试加锁,只有一个线程可以锁成功,成功的标志是lock()函数返回了;如果没锁成功,那么线程会卡在lock()不断地去尝试加锁。

unlock()用于解锁。

lock()和unlock()要成对使用。

class A {
public:
	//收到的消息放入到一个容器中
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;

			my_mutex.lock();
			msgRecvQueue.push_back(i);//假设数字i就是收到的消息,放入消息队列中
			my_mutex.unlock();
		}
	}

	bool outMsg(int &msg) {
		my_mutex.lock();
		if (!msgRecvQueue.empty()) {
			//消息队列不为空
			msg = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			my_mutex.unlock();
			return true;
		}
		my_mutex.unlock();
		return false;
	}

	//把消息从容器中取出
	void outMsgRecvQueue() {
		int msg = 0;
		for (int i = 0; i < 10000; i++) {
			bool result = outMsg(msg);
			
			if (result) {
				cout << "outMsgRecvQueue()执行,取出一个元素 " << msg << endl;

				//处理消息...

			}
			else {
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:
	list<int> msgRecvQueue;//专门用于存收到的消息的容器(消息队列)
	mutex my_mutex;//互斥量
};

int main() {

	A myobj;
	thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);//第二个参数加&保证在线程中用的是同一个对象
	thread myInMsgObj(&A::inMsgRecvQueue, &myobj);

	myOutMsgObj.join();
	myInMsgObj.join();

	cout << "main" << endl;
	return 0;
}

std::lock_guard 类模板

可以直接取代lock()和unlock(),具有创建时加锁,析构时解锁的功能(无需手动解锁)

用了lock_guard以后,就不能使用lock()和unlock()

lock_guard原理是构造函数里执行了lock(),析构函数里执行了unlock()

class A {
public:
	//收到的消息放入到一个容器中
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;

			my_mutex.lock();
			msgRecvQueue.push_back(i);//假设数字i就是收到的消息,放入消息队列中
			my_mutex.unlock();
		}
	}

	bool outMsg(int &msg) {
		lock_guard<mutex> guard(my_mutex);
		if (!msgRecvQueue.empty()) {
			//消息队列不为空
			msg = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			return true;
		}
		return false;
	}

	//把消息从容器中取出
	void outMsgRecvQueue() {
		int msg = 0;
		for (int i = 0; i < 10000; i++) {
			bool result = outMsg(msg);
			
			if (result) {
				cout << "outMsgRecvQueue()执行,取出一个元素 " << msg << endl;

				//处理消息...

			}
			else {
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:
	list<int> msgRecvQueue;//专门用于存收到的消息的容器(消息队列)
	mutex my_mutex;//互斥量
};

int main() {

	A myobj;
	thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);//第二个参数加&保证在线程中用的是同一个对象
	thread myInMsgObj(&A::inMsgRecvQueue, &myobj);

	myOutMsgObj.join();
	myInMsgObj.join();

	cout << "main" << endl;
	return 0;
}

std::lock() 同时锁多个互斥量

mutex mutex1;
mutex mutex2;

//std::lock() 函数可接受任意数量的互斥量, 该函数要么对所有参数互斥量都成功上锁,要么一个也不上锁。
//可在一定程度上避免死锁
lock(mutex1, mutex2);//同时锁mutex1和mutex2

mutex1.unlock();
mutex2.unlock();

std::adopt_lock

std::adopt_lock是个结构体对象,起一个标记作用。
作用就表示这个互斥量已经lock(),不需要在std::lock_guard的构造函数里对mutex类对象再lock()了。

mutex mutex1;
mutex mutex2;

lock(mutex1, mutex2);//同时锁mutex1和mutex2

lock_guard<mutex> guard1(mutex1, adopt_lock);
lock_guard<mutex> guard2(mutex2, adopt_lock);
//mutex1.unlock();
//mutex2.unlock();

unique_lock

unique_lock是个类模板,对互斥量进行加锁和解锁管理。

unique_lock比lock_guard灵活很多,但是效率差一点,内存占用多一点。

第二个参数缺省情况下,unique_lock和lock_guard一样

class A {
public:
	//收到的消息放入到一个容器中
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;

			unique_lock<mutex> u(my_mutex);
			msgRecvQueue.push_back(i);//假设数字i就是收到的消息,放入消息队列中
		}
	}

	bool outMsg(int &msg) {
		unique_lock<mutex> u(my_mutex);
		if (!msgRecvQueue.empty()) {
			//消息队列不为空
			msg = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			return true;
		}
		return false;
	}

	//把消息从容器中取出
	void outMsgRecvQueue() {
		int msg = 0;
		for (int i = 0; i < 10000; i++) {
			bool result = outMsg(msg);

			if (result) {
				cout << "outMsgRecvQueue()执行,取出一个元素 " << msg << endl;

				//处理消息...

			}
			else {
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:
	list<int> msgRecvQueue;//专门用于存收到的消息的容器(消息队列)
	mutex my_mutex;//互斥量
};

int main() {

	A myobj;
	thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);//第二个参数加&保证在线程中用的是同一个对象
	thread myInMsgObj(&A::inMsgRecvQueue, &myobj);

	myOutMsgObj.join();
	myInMsgObj.join();

	cout << "main" << endl;
	return 0;
}

unique_lock第二个参数

std::adopt_lock

std::adopt_lock是一个标记,表示这个互斥量已经lock,不用在构造函数中lock。

class A {
public:
	//收到的消息放入到一个容器中
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;

			my_mutex.lock();//提前加锁
			unique_lock<mutex> u(my_mutex,adopt_lock);
			msgRecvQueue.push_back(i);//假设数字i就是收到的消息,放入消息队列中
		}
	}

	bool outMsg(int &msg) {
		my_mutex.lock();
		unique_lock<mutex> u(my_mutex,adopt_lock);
		if (!msgRecvQueue.empty()) {
			//消息队列不为空
			msg = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			return true;
		}
		return false;
	}

	//把消息从容器中取出
	void outMsgRecvQueue() {
		int msg = 0;
		for (int i = 0; i < 10000; i++) {
			bool result = outMsg(msg);

			if (result) {
				cout << "outMsgRecvQueue()执行,取出一个元素 " << msg << endl;

				//处理消息...

			}
			else {
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:
	list<int> msgRecvQueue;//专门用于存收到的消息的容器(消息队列)
	mutex my_mutex;//互斥量
};

int main() {

	A myobj;
	thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);//第二个参数加&保证在线程中用的是同一个对象
	thread myInMsgObj(&A::inMsgRecvQueue, &myobj);

	myOutMsgObj.join();
	myInMsgObj.join();

	cout << "main" << endl;
	return 0;
}

std::try_to_lock

使用try_to_lock会尝试用mutex的lock()去锁定这个mutex,但如果没有锁定成功,也会立即返回,并不会阻塞在那里。

用try_to_lock的前提是不能先去lock互斥量。

class A {
public:
	//收到的消息放入到一个容器中
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {

			unique_lock<mutex> u(my_mutex,try_to_lock);
			if (u.owns_lock()) {
				//拿到了锁,操作共享数据
				cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
				msgRecvQueue.push_back(i);
			}
			else {
                //没有拿到锁
				cout << "inMsgRecvQueue()没拿到锁" << endl;
			}
		}
	}

	bool outMsg(int &msg) {

		unique_lock<mutex> u(my_mutex);

		chrono::milliseconds dura(200);
		this_thread::sleep_for(dura);//休息200毫秒

		if (!msgRecvQueue.empty()) {
			//消息队列不为空
			msg = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			return true;
		}
		return false;
	}

	//把消息从容器中取出
	void outMsgRecvQueue() {
		int msg = 0;
		for (int i = 0; i < 10000; i++) {
			bool result = outMsg(msg);

			if (result) {
				cout << "outMsgRecvQueue()执行,取出一个元素 " << msg << endl;

				//处理消息...

			}
			else {
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:
	list<int> msgRecvQueue;//专门用于存收到的消息的容器(消息队列)
	mutex my_mutex;//互斥量
};

int main() {

	A myobj;
	thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);//第二个参数加&保证在线程中用的是同一个对象
	thread myInMsgObj(&A::inMsgRecvQueue, &myobj);

	myOutMsgObj.join();
	myInMsgObj.join();

	cout << "main" << endl;
	return 0;
}

std::defer_lock

defer_lock意思是构造函数的时候不需要给mutex加锁。

class A {
public:
	//收到的消息放入到一个容器中
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;

			unique_lock<mutex> u(my_mutex, defer_lock);//没有对my_mutex加锁
			u.lock();//加锁,不用关心解锁
			msgRecvQueue.push_back(i);
		}
	}

	bool outMsg(int &msg) {

		unique_lock<mutex> u(my_mutex);

		if (!msgRecvQueue.empty()) {
			//消息队列不为空
			msg = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			return true;
		}
		return false;
	}

	//把消息从容器中取出
	void outMsgRecvQueue() {
		int msg = 0;
		for (int i = 0; i < 10000; i++) {
			bool result = outMsg(msg);

			if (result) {
				cout << "outMsgRecvQueue()执行,取出一个元素 " << msg << endl;

				//处理消息...

			}
			else {
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:
	list<int> msgRecvQueue;//专门用于存收到的消息的容器(消息队列)
	mutex my_mutex;//互斥量
};

int main() {

	A myobj;
	thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);//第二个参数加&保证在线程中用的是同一个对象
	thread myInMsgObj(&A::inMsgRecvQueue, &myobj);

	myOutMsgObj.join();
	myInMsgObj.join();

	cout << "main" << endl;
	return 0;
}

unique_lock的成员函数

lock()

给绑定的互斥量加锁。

unlock()

给绑定的互斥量解锁。

可以手动unlock(),unique_lock在析构的时候会判断对应的mutex有没有unlock,如果已经unlock,就不会再次unlock

try_lock()

尝试给互斥量加锁,如果拿不到锁,则返回false,如果拿到了锁,则返回true,这个函数不会阻塞。

release()

返回它所管理的mutex对象指针,并释放所有权。

unique_lock和mutex不再有关系。

如果原来mutex处于加锁状态,断开关系以后需要手动解锁。

unique_lock所有权的传递

unique_lockmyUniLock(myMutex);把myMutex和myUniLock绑定在了一起,也就是myUniLock拥有myMutex的所有权

使用移动语义

  • myUniLock拥有myMutex的所有权,myUniLock可以把自己对myMutex的所有权转移,但是不能复制。
  • unique_lock myUniLock2(std::move(myUniLock));
    现在myUniLock2拥有myMutex的所有权。

在函数中return一个临时变量

unique_lock<mutex> aFunction()
{
    unique_lock<mutex> myUniLock(myMutex);
    //返回这种局部对象会导致系统生成临时的unique_lock对象,并调用unique_lock的移动构造函数
    return myUniLock;
}
// 然后就可以在外层调用,在sbguard具有对myMutex的所有权
std::unique_lock<std::mutex> sbguard = aFunction();

你可能感兴趣的:(C++,c++,开发语言,算法)