C++ multi-threaded learning (seven, unique_lock)

Table of contents

unique_lock

unique_lock locking process

Use adopt_lock in unique_lock

Use defer_lock in unique_lock

Use try_to_lock in unique_lock


unique_lock

1. Correct locking order: When multiple unique_lock objects are used to lock multiple mutexes, the same locking order should be maintained to avoid deadlock.

2. Exception security: unique_lock provides exception security, that is, when an exception occurs, the mutex will be automatically released.

        Therefore, when using unique_lock , you can safely use RAII (resource acquisition is initialization) technology to avoid forgetting to release the lock.

3. Select an appropriate locking strategy: unique_lock provides two locking strategies, namely defer_lock and try_to_lock .

        When choosing a locking strategy, you need to consider whether you want to avoid blocking threads, or you need to retry the lock under certain conditions.

4. Locking granularity: When using unique_lock , the locking granularity should be reduced as much as possible to avoid unnecessary performance overhead. Only lock the mutex when necessary.

5. Avoid unnecessary locking: unique_lock provides lock and unlock member functions to manually control locking and unlocking. When in use, unnecessary locking and unlocking operations should be avoided to improve performance.

6. Select an appropriate mutex: When choosing to use unique_lock , you need to select an appropriate mutex type according to your specific needs.

        For example, if you need to support recursive locking, you can choose std:: recursive_mutex ; if you need to support multiple threads to read shared data at the same time, you can choose std:: shared_mutex , etc.

unique_lock locking process

#include<iostream>
#include<thread>
#include<mutex>
#include<list>
using namespace std;
class Things
{
public:
	void goToilet()
	{
		for (int i = 0; i < 10000; i++)
		{
			unique_lock<mutex> unique(mtx);//一样是在构造函数和析构函数中进行加锁和解锁的过程
			cout << "上厕所" << endl;
			num.push_back(i);
		}
	}
	void goBath()
	{
		for (int i = 0; i < 10000; i++)
		{
			if (!num.empty())
			{
				unique_lock<mutex> unique(mtx);
				cout << "洗澡" << endl;
				num.pop_back();
			}
			else
			{
				cout << "干其他事情" << endl;
			}
		}
	}
protected:
	list<int> num;
	mutex mtx;
};
int main()
{
	Things DoSomeThing;
	thread t1(&Things::goToilet, &DoSomeThing);
	thread t2(&Things::goBath, &DoSomeThing);
	t1.join();
	t2.join();

	return 0;
}

Use adopt_lock in unique_lock

Lock is required before using adopt_lock , otherwise abort will be executed to terminate the program.

#include<iostream>
#include<thread>
#include<mutex>
#include<list>
using namespace std;
class Things
{
public:
	void goToilet()
	{
		for (int i = 0; i < 10000; i++)
		{
			mtx.lock();//使用adopt_lock需要先进行上锁
			unique_lock<mutex> unique(mtx,adopt_lock);
			cout << "上厕所" << endl;
			num.push_back(i);
		}
	}
	void goBath()
	{
		for (int i = 0; i < 10000; i++)
		{
			if (!num.empty())
			{
				mtx.lock();//使用adopt_lock需要先进行上锁
				unique_lock<mutex> unique(mtx, adopt_lock);
				cout << "洗澡" << endl;
				num.pop_back();
			}
			else
			{
				cout << "干其他事情" << endl;
			}
		}
	}
protected:
	list<int> num;
	mutex mtx;
};
int main()
{
	Things DoSomeThing;
	thread t1(&Things::goToilet, &DoSomeThing);
	thread t2(&Things::goBath, &DoSomeThing);
	t1.join();
	t2.join();

	return 0;
}

Use defer_lock in unique_lock

Using defer_lock is to install an unlocked lock for you, and you need to manually lock it yourself.

It is better to perform micro-manipulation locally.

#include<iostream>
#include<thread>
#include<mutex>
#include<list>
using namespace std;
class Things
{
public:
	void goToilet()
	{
		for (int i = 0; i < 10000; i++)
		{
			unique_lock<mutex> unique(mtx,defer_lock);//没有上锁的锁
			unique.lock();
			cout << "上厕所" << endl;
			unique.unlock();
			num.push_back(i);
			unique.lock();
		}
	}
	void goBath()
	{
		for (int i = 0; i < 10000; i++)
		{
			if (!num.empty())
			{
				unique_lock<mutex> unique(mtx);
				cout << "洗澡" << endl;
				num.pop_back();
			}
			else
			{
				cout << "干其他事情" << endl;
			}
		}
	}
protected:
	list<int> num;
	mutex mtx;
};
int main()
{
	Things DoSomeThing;
	thread t1(&Things::goToilet, &DoSomeThing);
	thread t2(&Things::goBath, &DoSomeThing);
	t1.join();
	t2.join();

	return 0;
}

Use try_to_lock in unique_lock

#include<iostream>
#include<thread>
#include<mutex>
#include<list>
using namespace std;
class Things
{
public:
	void goToilet()
	{
		for (int i = 0; i < 10000; i++)
		{
			unique_lock<mutex> unique(mtx,try_to_lock);//try_to_lock测试去锁
			if (unique.owns_lock())//判断能不能成功的获取到锁
			{
				cout << "上厕所" << endl;
				num.push_back(i);
			}
		}
	}
	void goBath()
	{
		for (int i = 0; i < 10000; i++)
		{
			if (!num.empty())
			{
				unique_lock<mutex> unique(mtx);
				cout << "洗澡" << endl;
				num.pop_back();
			}
			else
			{
				cout << "干其他事情" << endl;
			}
		}
	}
protected:
	list<int> num;
	mutex mtx;
};
int main()
{
	Things DoSomeThing;
	thread t1(&Things::goToilet, &DoSomeThing);
	thread t2(&Things::goBath, &DoSomeThing);
	t1.join();
	t2.join();

	return 0;
}

Guess you like

Origin blog.csdn.net/q244645787/article/details/131581344