一、概念
临界区又称关键代码段,指的是一小段代码在执行前,需要独占一些资源。程序中通常将多线程同时访问的某个资源作为临界区,需要定义一个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;
}