C++多线程与mutex

目录

 

线程的创建

1.对函数创建线程

​2.对类里面创建线程

3.对成员函数创建变量

4.多线程的创立

Mutex

1.一般互斥锁

2.lock_guard

3.unique_lock 

条件变量


线程的创建

线程的创建需要#include <thread>,格式为thread mythread(....)

1.对函数创建线程

格式为thread mythread(function,variable1,variable2,...)

#include <iostream>
#include <thread>
using namespace std;
void fuction(int a, int b) {
	cout << "enter the subthread:";
	cout << a << b << endl;
}
int main(void) {
	cout << "in main thread" << endl;
	thread mythread(fuction, 2, 9);
	mythread.join();
	cout << "in main thread" << endl;
}

2.对类里面创建线程

格式为thread mythread(classvariable,variables);这里似乎一定要重载括号。

#include <iostream>
#include <thread>
#include <string>
using namespace std;
class X
{
public:
	X() { cout << "constructor!" << endl; }
	X(const X& m) { cout << "copy constructor called !" << endl; }
	void operator()(int a)   // 重载括号运算符。如果无参则为void operator()(){...}
	{
		cout << "start!" << a << endl;
	}
};

int main()
{
	X a;
	thread p(a, 4);  //以对象a创建线程,并传递参数4,无参的话直接省略第二个参数,更多的参数直接加在后面
	p.join();
	return 0;
}

3.对成员函数创建变量

格式为thread mythread(&class::function,&classvariable,variables_of_function)

#include <iostream>
#include <thread>
#include <string>
using namespace std;
class X
{
public:
	X() { cout << "constructor!" << endl; }
	X(const X& m) { cout << "copy constructor called !" << endl; }
	void coutdata(int a, int d) {
		cout << a << '&' << d << endl;
	}
};

int main()
{
	X a;
	thread p(&X::coutdata,&a,3,4);  //以对象coutdata创建线程,并传递参数3、4,
									//无参的话直接省略第二个参数,更多的参数直接加在后面
	p.join();
	return 0;
}

 

4.多线程的创立

只要同时使用多个thread创建,再逐一join便可。mythread.join();表示阻塞主线程,等子线程运行完再运行主线程,可join多个子线程。这时便进行了多线程运行,如果两个线程同时对一个变量进行操作则需要下面的mutex。C++ 多线程join()和detach()的理解_皮皮#2500-CSDN博客_c++线程detach

Mutex

锁是把在lock和unlock之间的代码锁起来,如果涉及到几个线程共同涉及到一个变量,锁便起了大作用。需要#include <mutex>。没有加锁:

#include <iostream>
#include <thread>
#include <string>
#include <mutex>
#include <list>
using namespace std;
class List {
private:
	list<int> mylist;
	mutex mlock;
	int i = 0;
public:
	void WriteList()
	{
		while (i < 1000)
		{
			mylist.push_back(i++);
		}
		return;
	}
	void showList()
	{
		for (auto p = mylist.begin(); p != mylist.end(); p++)
		{
			cout << (*p) << " ";
		}
		cout << endl;
		cout << "size of list : " << mylist.size() << endl;
		return;
	}
};
int main()
{
	List mlist;
	thread pwrite0(&List::WriteList, &mlist);
	thread pwrite1(&List::WriteList, &mlist);

	pwrite0.join();
	pwrite1.join();
	cout << "threads end!" << endl;

	mlist.showList();  //子线程结束后主线程打印list
	return 0;
}

需要注意的是:这里的.join()只是阻塞主线程,不对其他线程进行阻塞,只与出来的顺序有关系。 

改自:

1.一般互斥锁

1. 原子性:把一个互斥量锁定为一个原子操作,这意味着如果一个线程锁定了一个互斥量,没有其他线程在同一时间可以成功锁定这个互斥量;

2. 唯一性:如果一个线程锁定了一个互斥量,在它解除锁定之前,没有其他线程可以锁定这个互斥量;

3. 非繁忙等待:如果一个线程已经锁定了一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程将被挂起(不占用任何cpu资源),直到第一个线程解除对这个互斥量的锁定为止,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。

 互斥锁将mlock.lock()和mlock.unlock()之间锁住;

#include <iostream>
#include <thread>
#include <string>
#include <mutex>
#include <list>
using namespace std;
class List {
private:
	list<int> mylist;
	mutex mlock;
	int i = 0;
public:
	void WriteList()
	{
		while (i < 1000)
		{
			mlock.lock();
			if (i >= 10000)
			{
				mlock.unlock();   //退出之前必须先解锁
				break;
			}
			mylist.push_back(i++);
			mlock.unlock();
		}
		return;
	}
	void showList()
	{
		for (auto p = mylist.begin(); p != mylist.end(); p++)
		{
			cout << (*p) << " ";
		}
		cout << endl;
		cout << "size of list : " << mylist.size() << endl;
		return;
	}
};
int main()
{
	List mlist;
	thread pwrite0(&List::WriteList, &mlist);
	thread pwrite1(&List::WriteList, &mlist);

	pwrite0.join();
	pwrite1.join();
	cout << "threads end!" << endl;

	mlist.showList();  //子线程结束后主线程打印list
	return 0;
}

2.lock_guard

lock_guard就是一个类,它会在lock_guard的构造函数中加锁,而在析构函数中解锁,也就是说,只要创建一个lock_guard的对象,就相当于lock()了,而该对象析构时,就自动调用unlock()了。

void WriteList()
	{
		while(i<10000)
		{
                        lock_guard<mutex> guard(mlock);  //创建lock_guard的类对象guard,用互斥量m来构造
			//mlock.lock();   
			if (i >= 10000)
			{
				//mlock.unlock();   //由于有了guard,这里就无需unlock()了
				break;
			}
			mylist.push_back(i++);
			//mlock.unlock();
		}
		return;
	}

3.unique_lock 

unique_lock和上面的规则差不多,不过他生命期内允许手动加锁和释放锁。图来自:

这里说明一下unique_lock是给原先有的一个mutex类型(比如叫mlock)再套上一层,

unique<mutex> locker(mlock)。使mlock变为名字为locker的特殊锁,对其解锁时用的是:

locker.unlock();而非mlock.unlock(); 

条件变量

copy_from

条件变量使用“通知—唤醒”模型,生产者生产出一个数据后通知消费者使用,消费者在未接到通知前处于休眠状态节约CPU资源;当消费者收到通知后,赶紧从休眠状态被唤醒来处理数据,使用了事件驱动模型,在保证不误事儿的情况下尽可能减少无用功降低对资源的消耗。

  • 那个通知“条件已满足”的线程(或多个线程之一)必须调用notify_one()或notify_all(),以便条件满足时唤醒处于等待中的一个条件变量;
  • 那个等待"条件被满足"的线程必须调用wait(),可以让线程在条件未被满足时陷入休眠状态,当接收到通知时被唤醒去处理相应的任务
#include <iostream>
#include <deque>	//双端队列
#include <thread>
#include <mutex>

using namespace std;
deque<int> q;
mutex mu;
condition_variable cond;

void productor() {
	int count = 10;
	while (count > 0) {
		unique_lock<mutex> locker(mu);
		q.push_front(count);
		locker.unlock();
		cond.notify_one();
		this_thread::sleep_for(chrono::seconds(1));
		count--;
	}
}

void consumer() {
	int data = 0;
	while (data != 1) {
		unique_lock<mutex> locker(mu);
		while (q.empty()) {
			cond.wait(locker);
		}
		data = q.back();
		q.pop_back();
		locker.unlock();
		std::cout << "t2 got a value from t1: " << data << std::endl;
	}
}
int main(void) {
	thread t1(productor);
	thread t2(consumer);
	t1.join();
	t2.join();
	
	return 0;
}

真实工程举例

在高翔博士orbslam2的稠密建图的版本中用到了很多锁。

这里的unique_lock以一个{}之内的东西认为是其作用区域,处理{}之后就析构。第一个锁是抢了关闭权,第二个conditional_variable等待关键帧的插入,第三个防止计算关键帧出错。。。

void PointCloudMapping::viewer()
{
    pcl::visualization::CloudViewer viewer("viewer");
    while(1)
    {
        {
            unique_lock<mutex> lck_shutdown( shutDownMutex );
            if (shutDownFlag)
            {
                break;
            }
        }
        {//锁:插入的时候我不进行
            unique_lock<mutex> lck_keyframeUpdated( keyFrameUpdateMutex );
            keyFrameUpdated.wait( lck_keyframeUpdated );
        }

        // keyframe is updated
        size_t N=0;
        {//锁:防止计算出错
            unique_lock<mutex> lck( keyframeMutex );
            N = keyframes.size();
        }

        for ( size_t i=lastKeyframeSize; i<N ; i++ )
        {
            PointCloud::Ptr p = generatePointCloud( keyframes[i], colorImgs[i], depthImgs[i] );
            *globalMap += *p;
        }
        PointCloud::Ptr tmp(new PointCloud());
        voxel.setInputCloud( globalMap );
        voxel.filter( *tmp );
        globalMap->swap( *tmp );
        viewer.showCloud( globalMap );
        cout << "show global map, size=" << globalMap->points.size() << endl;
        lastKeyframeSize = N;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值