C++ 线程同步---临界区&自旋锁

一、概念
临界区又称关键代码段,指的是一小段代码在执行前,需要独占一些资源。程序中通常将多线程同时访问的某个资源作为临界区,需要定义一个CRITICAL_SECTION类型的变量,然后调用InitializeCriticalSection的函数对变量进行初始化,只能用于单进程。临界区是非内核对象,只在用户态进行锁操作,速度快;互斥体是内核对象,在核心态进行锁操作,速度慢。
目的:防止数据一致性被破坏。
函数声明:

//初始化CRITICAL_SECTION变量 
//lpCriticalSection:一个CRITICAL_SECTION结构指针,表示用于初始化的临界区;
VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection )//判断是否有线程 在访问临界区资源,没有就改变CRITICAL_SECTION成员的值
//有线程在访问,就进入自旋状态
VOID WINAPI EnterCriticalSection(__inout LPCRITICAL_SECTION lpCriticalSection);
//释放资源函数
void WINAPI LeaveCriticalSection( _Inout_LPCRITICAL_SECTION lpCriticalSection);
//释放CRITICAL_SECTION结构体指针
void WINAPI DeleteCriticalSection(_Inout_ LPCRITICAL_SECTION lpCriticalSection);

二、自旋锁
程序访问共享资源的时候,未获得锁的线程处于等待状态,这个等待状态就是自旋,他区分与互斥锁。
互斥锁:(放弃cpu)
互斥锁等不到锁时,线程会进入休眠,一旦资源被占用都会产生任务切换,任务切换涉及很多东西包括:保存原来的上下文,按调度算法选择新的任务,恢复新任务的上下文,修改cr3寄存器等都需要大量的时间;一旦涉及堵塞代价十分昂贵。
自旋锁(忙等,一直占着cpu)
当锁被其他线程占有时,获取锁的线程便会进入自旋,不断检测自旋锁的状态,一旦自旋锁被释放,线程便结束自旋,得到自旋锁的线程便可以执行临界区的代码,对于临界区的代码必须短小,否则其他线程会一直阻塞。
三、示例
临界区
临界区对象

#ifndef CAUTO_LOCK_H__
#define CAUTO_LOCK_H__
 
class CAutoLock
{
public:
	CAutoLock();
	~CAutoLock();
 
	void Lock();
	void UnLock();
 
private:
	CRITICAL_SECTION m_Section;
};
 
#endif
#include "stdafx.h"
#include "CAutoLock.h"
 
CAutoLock::CAutoLock()
{
	InitializeCriticalSection(&m_Section);
	//Lock();如果是用的时候只定义锁对象,可以不手动进入临界区和退出临界区
}
 
CAutoLock::~CAutoLock()
{
	DeleteCriticalSection(&m_Section);
	//UnLock();
}
 
void CAutoLock::Lock()
{
	EnterCriticalSection(&m_Section);
}
 
void CAutoLock::UnLock()
{
	LeaveCriticalSection(&m_Section);
}

三个线程创建类

#include "stdafx.h"
#include "CAutoLock.h"
 
CAutoLock::CAutoLock()
{
	InitializeCriticalSection(&m_Section);
	//Lock();如果是用的时候只定义锁对象,可以不手动进入临界区和退出临界区
}
 
CAutoLock::~CAutoLock()
{
	DeleteCriticalSection(&m_Section);
	//UnLock();
}
 
void CAutoLock::Lock()
{
	EnterCriticalSection(&m_Section);
}
 
void CAutoLock::UnLock()
{
	LeaveCriticalSection(&m_Section);
}

#include "stdafx.h"
#include "CCriticalSection.h"
#include <iostream>
using namespace std;
 
int TestCriticalSection::m_nTotals = 0;//初始化静态成员变量
 
TestCriticalSection::TestCriticalSection()
{
	m_nTotals = 0;
        m_hThread1 = INVALID_HANDLE_VALUE;
	m_hThread2 = INVALID_HANDLE_VALUE;
}
 
TestCriticalSection::~TestCriticalSection()
{
	if (m_hThread1 != NULL)
	{
		CloseHandle(m_hThread1);
		m_hThread1 = NULL;
	}
 
	if (m_hThread2 != NULL)
	{
		CloseHandle(m_hThread2);
		m_hThread2 = NULL;
	}
 
	if (m_hThread3 != NULL)
	{
		CloseHandle(m_hThread3);
		m_hThread3 = NULL;
	}
}
 
DWORD __stdcall TestCriticalSection::ThreadFun1(LPVOID lParam) //static只需要加在类定义里,类定义外面的函数定义前不能写static
{
	DWORD dRet = TRUE;
	TestCriticalSection * pThis = static_cast<TestCriticalSection*>(lParam);
	while(1)
	{
		pThis->m_lock.Lock();
		pThis->m_nTotals ++;
		cout<<"ThreadFun1: m_nTotals "<<pThis->m_nTotals<<endl;
		
		if (pThis->m_nTotals >= 30)
		{
			break;
		}
                pThis->m_lock.UnLock();
		Sleep(10);
	}
 
	return dRet;
}
 
DWORD __stdcall TestCriticalSection::ThreadFun2(LPVOID lParam)
{
	DWORD dRet = TRUE;
	TestCriticalSection * pThis = static_cast<TestCriticalSection*>(lParam);
	while(1)
	{
		pThis->m_lock.Lock();
		pThis->m_nTotals ++;
		cout<<"ThreadFun2: m_nTotals "<<pThis->m_nTotals<<endl;
		
		if (pThis->m_nTotals >= 30)
		{
			break;
		}
                pThis->m_lock.UnLock();
		Sleep(10);
	}
	return dRet;
}
 
DWORD __stdcall TestCriticalSection::ThreadFun3(LPVOID lParam)
{
	DWORD dRet = TRUE;
	TestCriticalSection * pThis = static_cast<TestCriticalSection*>(lParam);
	while(1)
	{
		pThis->m_lock.Lock();
		pThis->m_nTotals ++;
		cout<<"ThreadFun3: m_nTotals "<<pThis->m_nTotals<<endl;
		
		if (pThis->m_nTotals >= 30)
		{
			break;
		}
                pThis->m_lock.UnLock();
		Sleep(10);
	}
 
	return dRet;
}
 
void TestCriticalSection::StartThread()
{
	m_hThread1 = CreateThread(NULL, 0, &TestCriticalSection::ThreadFun1, this,  0, NULL);
	m_hThread2 = CreateThread(NULL, 0, &TestCriticalSection::ThreadFun2, this, 0, NULL);
	m_hThread3 = CreateThread(NULL, 0, &TestCriticalSection::ThreadFun3, this, 0, NULL);
}

主函数

#include "stdafx.h"
#include "CCriticalSection.h"
 
int _tmain(int argc, _TCHAR* argv[])
{
	TestCriticalSection  CriticalSectionObj;
	CriticalSectionObj.StartThread();
	Sleep(5000);
	system("pause");
	return 0;
}

自旋锁示例

#include "pch.h"
#include <iostream>
#include <atomic>
#include <thread>
#include <chrono>

using namespace std;

class SpinLock {
public:	
	inline void Lock() {
		while (m_flag.test_and_set()) {
			std::this_thread::yield();
		}
	}
	inline void UnLock() { m_flag.clear(); }
private:
	//c++11提供的原子操作
	std::atomic_flag m_flag = ATOMIC_FLAG_INIT;
};

void fun1(int i,SpinLock& lock) {
	while (1) {
		lock.Lock();
		std::cout << "threadId:" << i << ":starting" << std::endl;
		std::this_thread::sleep_for(std::chrono::microseconds(100));
		lock.UnLock();
	}
	
}

void fun2(int i,SpinLock& lock) {
	while (1) {
		lock.Lock();
		std::cout << "threadId:" << i << ":starting" << std::endl;
		std::this_thread::sleep_for(std::chrono::milliseconds(100));
		lock.UnLock();
	}
}

int main()
{
	SpinLock lock;
	std::thread t1(fun1,1,std::ref(lock));
	std::thread t2(fun2, 2, std::ref(lock));
	t1.join();
	t2.join();
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值