c++互斥锁,竞争状态与临界区

1,基本互斥锁

//互斥锁使用
#include<mutex>
static  mutex  mux;
mux.lock();
mux.unlock();

案例

// An highlighted block
#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static  mutex  mux;

void  TestThread1()
{
	mux.lock();
	cout << "=====================================" << endl;
	cout << "test 001" << endl;
	cout << "test 002" << endl;
	cout << "test 003" << endl;
	cout << "=====================================" << endl;
	mux.unlock();
	std::this_thread::sleep_for(std::chrono::milliseconds(50));
}

void deal1()
{
    thread  th(TestThread1);
    th.join();
}

int  main()
{
	deal1();
}

运行结果
在这里插入图片描述
互斥锁的作用在于可以尽可能的避免数据竞争,在lockunlock之间的区域被称之为临界区,要注意一点的是,临界区要尽量小,锁要尽早的申请且尽早的释放

2,try_lock

if (!mux.try_lock())
	{

	}
//尝试去锁,成功返回true,失败返回false

案例:

#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static  mutex  mux;

void  TestThread1()
{
	for (;;)
	{
		if (!mux.try_lock())
		{
			cout << "." << "flush" << endl;;
			this_thread::sleep_for(std::chrono::milliseconds(50));
			continue;
		}
		
		//mux.lock();
		cout << "=====================================" << endl;
		cout << "test 001" << endl;
		cout << "test 002" << endl;
		cout << "test 003" << endl;
		cout << "=====================================" << endl;
		mux.unlock();
		std::this_thread::sleep_for(std::chrono::milliseconds(50));
	}
	
}

void  deal1()
{
	for (int i = 0;i<10;i++)
	{
		thread  th1(TestThread1);
		th1.detach();
	}
	getchar();
}
int  main()
{
	deal1();
}

注意,try_lock有性能开销。

3,互斥锁存在的坑—线程抢占不到资源

在2节中,unlock之后选择让线程睡眠50毫秒,为什么要要sleep 50毫秒呢???这就涉及到多线程并发时因多线程竞争cpu资源而容易出现的坑。

线程有这样几种状态,
1,初始化(Init): 表示该线程正在创建。
2,就绪(Ready):表示该线程在就绪列表中,等待CPU调度。
3,运行(Running): 表示该线程正在运行。
4,阻塞(Blocked):该线程被阻塞挂起,包括,锁,事件,信号量阻塞。此状态不占用cpu资源
5,退出:该线程结束,等待父线程回收资源。

在这里插入图片描述
一个线程在创建,就绪之后,肯直接进入阻塞状态,也可能先进入运行状态,后进入阻塞状态。当由运行状态进入阻塞状态之后,线程在阻塞一段时间之后可能因为等待互斥锁或条件变量,重新进入就绪态,等待cpu调度之后,才再次进入运行状态。

实际上,多线程并发运行,在某一个时间段之内其实只有一个线程在占用cpu资源,当某一个线程调用unlock之后,所有线程会再次去争夺cpu资源,此时会出现一个神奇的现象,那就是,某一个线程在unlock之后,cpu资源被释放,然后多个线程再次竞争cpu资源的时候,上一个占用cpu资源的线程再次获取到了cpu资源,导致某一个线程长期能抢占到cpu资源而其他线程就无法顺利运行下去,这种情况是我们在实际的项目当中不想遇到的,于是乎,在调用unlock之后,让现场 sleep 一段事件,这样就可以让其他线程争夺cpu资源,就不会出现某一个线程长期占用cpu资源的场景了。

4,超时锁

static timed_mutex  tmux;
if (!!tmux.try_lock_for(chrono::milliseconds(500)))
	{

	}
//尝试获取锁,当五百毫秒内没有获取到锁资源,就会返回false

案例:

#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static timed_mutex  tmux;

void  ThewadMain(int i)
{
	for (;;)
	{
		if (!tmux.try_lock_for(chrono::milliseconds(500)))
		{
			cout << i << "  lock failed " << endl;
			continue;
		}
		else
		{
			cout << i << " [in]" << endl;
			tmux.unlock();
			std::this_thread::sleep_for(chrono::microseconds(2000));
		}
	}
}

void  deal1()
{
	for (int i = 0; i < 3; i++)
	{
		thread th(ThewadMain,i);
		th.detach();
	}
}

int  main()
{
	deal1();
	getchar();
}

运行结果:
在这里插入图片描述

5,递归锁(在一个线程内可以多次lock的锁)recursive_mutex和recursive_timed_mutex用于业务组合

在我们开发某个项目的时候,随着时间的推移,项目代码越来越多,接口越来越多,会出现大量的接口层层调用的情况,肯会出现的一个情况是,某个外部接口已经对某个锁进行了 lock 但是调用的两一个接口也对某一个锁进行了lock ,同一个锁在同一个线程内连续调用两次lock会发生什么现象呢?

// An highlighted block
#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static mutex  mux;


void Task1()
{
	mux.lock();
	cout << "task1" << endl;
	mux.unlock();
}


void  Task2()
{
	mux.lock();
	cout << "task2" << endl;
	mux.unlock();
}


void  ThewadMain(int i)
{
	mux.lock();
	Task1();
	Task2();
	mux.unlock();
}

void  deal1()
{
	thread th1(ThewadMain,1);
}

int  main()
{
	deal1();
	getchar();
}

在这里插入图片描述
我们发现,程序直接崩溃无法运行。
为了解决这个问题我们引入了递归锁,所谓的递归锁就是可以在一个线程中锁多次。

static recursive_mutex  rmux;
#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>

using namespace std;
static recursive_mutex  rmux;

void Task1()
{
	rmux.lock();
	cout << "task1" << endl;
	rmux.unlock();
}


void  Task2()
{
	rmux.lock();
	cout << "task2" << endl;
	rmux.unlock();
}


void  ThewadMain(int i)
{
	rmux.lock();
	Task1();
	Task2();
	rmux.unlock();
}

void  deal1()
{
	thread th1(ThewadMain,1);
	th1.detach();
}

int  main()
{
	deal1();
	getchar();
}

在这里插入图片描述
递归锁可以多次lock,但是也要相应的进行多次unlock,比如我lock两次,那么我也要相应的unlock两次。

6,利用栈特性自动释放锁RALL

RALL c++之父提出,使用局部对象来管理资源的技术成为资源获取即初始化,它的生命周期是由操作系统来管理的,无需人工介入;资源的销毁容易忘记,造成死锁或内存泄露。

简单来说就是,我们自己管理锁需要手动 lock 或者 手动 unlock ,但是随着项目周期的拉长我们很可能忘记 unlock 造成死锁,此时我们用一种方法可以不用手动实现 lock unlock 即可实现自动释放,尽可能的避免死锁或内存泄露。

6.1手动实现简易RALL

#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>

using namespace std;
static mutex  rmux;


class  XMutex
{
public:
	XMutex(mutex& mux):mux_(mux)
	{
		mux_.lock();
	}
	~XMutex()
	{
		mux_.unlock();
	}
private:
	mutex& mux_;
};

void  ThreadMain(int i)
{
	XMutex  lock(rmux);
	cout << i<<" in thread" << endl;
}


void  deal1()
{
	for (int i = 0; i < 10; i++)
	{
		thread th(ThreadMain,i);
		th.detach();
	}
}


int  main()
{
	deal1();
	getchar();
}

在这里插入图片描述

6.2 c++11支持的RALL管理互斥资源 lock_guard

// A code block
static mutex  rmux;
lock_guard<mutex> lock(rmux);
#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static mutex  rmux;

void  ThreadMain(int i)
{
	{
		lock_guard<mutex> lock(rmux);
		cout << i << " in thread" << endl;
	}
	std::this_thread::sleep_for(chrono::microseconds(200));
	
}


void  deal1()
{
	for (int i = 0; i < 10; i++)
	{
		thread th(ThreadMain,i);
		th.detach();
	}
}


int  main()
{
	deal1();
	getchar();
}

如上述代码所示,当出了大括号的范围,自动调用析构函数,会自动进行unlock,。

6.3 c++11 unique_lock c++11

上一个小结的 lock_guard 是一个最基本的简单的 RALL 锁,但实际的项目开发过程会出现更复杂的业务需求,比如会存在 一个锁赋值给另一个锁,一个锁移动到另一个锁,或者可能会出现临时手动解锁加锁,或者其他更复杂的需求,此时我们就会使用 unique_lock 。

// A code block
unique_lock  c++11  
支持临时释放锁 unlock
支持 adopt_lock (已经拥有锁,不加锁,出栈区会释放)
支持 defer_lock (延后拥有,不加锁,出栈区不释放)
支持 try_to_lock 尝试获得互斥的所有权而不阻塞,获取失败推出栈区不会释放,通过 owns_lock() 函数判断。
// An highlighted block
#include<iostream>
#include<thread>
#include<string>
#include<windows.h>
#include<chrono>
#include<mutex>
using namespace std;
static mutex  rmux;

void  ThreadMain(int i)
{
	{
		unique_lock<mutex> lock(rmux);
		cout << i << " in thread" << endl;
	}
	std::this_thread::sleep_for(chrono::microseconds(200));
	
}


void  deal1()
{
	for (int i = 0; i < 10; i++)
	{
		thread th(ThreadMain,i);
		th.detach();
	}
}


int  main()
{
	deal1();
	getchar();
}

支持临时释放锁

// An highlighted block
void  ThreadMain(int i)
{
	{
		unique_lock<mutex> lock(rmux);
		lock.unlock();
		cout << i << " in thread" << endl;
		lock.lock();
	}
	std::this_thread::sleep_for(chrono::microseconds(200));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值