1
、临界区
临界区
指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个线程访问的特性。当有线程进入临界区段时,其他线程或是进程必须等待(例如:
bounded waiting
等待法),有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共用资源是被互斥获得使用。
简单来说,临界区是一种锁,每次只允许一把钥匙对锁进行加锁操作。程序在使用临界资源之前需要使用自己的钥匙对锁进行加锁操作。加锁操作时,先检查锁是否处于锁定操作,如果处于锁定状态,那么需要等待锁被原加锁者解锁或超时。加锁成功后,程序可以使用临界资源。在使用完成之后需要使用钥匙对资源进行解锁。从而保证,同一时刻只有一个用户能够对临界资源进行访问。
临界区
实现的是一种
互斥
的保护。
2
、
Windows
临界区
&esmp;windows
提供了一种
CRITICAL_SECTION
结构,并提供相关操作函数来实现相关功能,主要包括:
//
初始化临界区
void
WINAPI
InitializeCriticalSection
(LPCRITICAL_SECTION lpCriticalSection);
//
带自旋次数的初始化临界区
BOOL WINAPI
InitializeCriticalSectionAndSpinCount
(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount);
//
设置临界区的自旋次数
DWORD WINAPI
SetCriticalSectionSpinCount
(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount);
//
尝试进入临界区
BOOL WINAPI
TryEnterCriticalSection
(LPCRITICAL_SECTION lpCriticalSection);
//
进入临界区
void
WINAPI
EnterCriticalSection
(LPCRITICAL_SECTION lpCriticalSection);
//
离开临界区
void
WINAPI
LeaveCriticalSection
(LPCRITICAL_SECTION lpCriticalSection);
//
删除临界区,释放临界区
void
WINAPI
DeleteCriticalSection
(LPCRITICAL_SECTION lpCriticalSection);
CRITICAL_SECTION
并不是
windows
系统的核心对象,不具备名称。因此,临界区只能用于单个进程的多个线程之间的同步,对于进程之间的通信和同步无能为力。
3
、
UE4
中的临界区
在
UE4
中,
Engine\Source\Runtime\Core\Public\Windows\WindowsCriticalSection.h
文件提供了
UE4
中临界区的定义,主要包括
FWindowsCriticalSection
和
FWindowsSystemWideCriticalSection
两种。
3.1 FWindowsCriticalSection
FWindowsCriticalSection
是
UE4
对
windows
操作系统中
CRITICAL_SECTION
结构的封装,提供无命名的临界区资源锁以及相关操作功能。
/**
* This is the Windows version of a critical section. It uses an aggregate
* CRITICAL_SECTION to implement its locking.
*/
class
FWindowsCriticalSection
{
//
私有成员,
windows
普通的
CRITICAL_SECTION
变量
Windows
::
CRITICAL_SECTION CriticalSection;
public:
//
构造函数,自旋次数初始值设置为
4000.
FORCEINLINE
FWindowsCriticalSection
()
{
CA_SUPPRESS
(
28125
);
//
初始化临界区资源
Windows
::
InitializeCriticalSection
(
&
CriticalSection);
Windows
::
SetCriticalSectionSpinCount
(
&
CriticalSection,
4000
);
}
//
析构函数,释放临界区
FORCEINLINE
~
FWindowsCriticalSection
()
{
Windows
::
DeleteCriticalSection
(
&
CriticalSection);
}
//
加锁操作,采用先尝试,后进入的流程
FORCEINLINE
void
Lock
()
{
// Spin first before entering critical section, causing ring-0 transition and context switch.
if
(Windows
::
TryEnterCriticalSection
(
&
CriticalSection)
==
0
)
{
Windows
::
EnterCriticalSection
(
&
CriticalSection);
}
}
//
尝试加锁,当前临界区能够被当前线程加锁,则返回
true
,否则返回
false
FORCEINLINE
bool
TryLock
()
{
if
(Windows
::
TryEnterCriticalSection
(
&
CriticalSection))
{
return
true
;
}
return
false
;
}
//
解锁
FORCEINLINE
void
Unlock
()
{
Windows
::
LeaveCriticalSection
(
&
CriticalSection);
}
private:
//
确保临界区无法被复制和赋值
FWindowsCriticalSection
(
const
FWindowsCriticalSection
&
);
FWindowsCriticalSection
&
operator=
(
const
FWindowsCriticalSection
&
);
};
通过将
FWindowsCriticalSection::FWindowsCriticalSection(const FWindowsCriticalSection&)
和
FWindowsCriticalSection& FWindowsCriticalSection::operator=(const FWindowsCriticalSection&)
声明为私有函数,从而确保临界区资源锁无法被复制和赋值。
3.2 FWindowsSystemWideCriticalSection
除了无法用于进程间同步的普通临界区之外,
UE4
还通过
Mutex
模仿了一个可用于进程间同步的临界区
FWindowsSystemWideCriticalSection
。
Mutex
是互斥器,在
windows
系统中是系统的核心对象,可以命名,并通过名称获取已经存在的对象实例。基于以上特性,
UE4
通过
Mutex
的封装,实现了能够用于进程间线程同步的
FWindowsSystemWideCriticalSection
,其类定义如下:
/** System-Wide Critical Section for windows using mutex */
class
CORE_API
FWindowsSystemWideCriticalSection
{
public:
//
构造函数,创建并尝试获取命名
Mutex
explicit
FWindowsSystemWideCriticalSection
(
const
class
FString
&
InName, FTimespan InTimeout
=
FTimespan
::
Zero
());
//
析构函数,如果当前持有锁的话释放锁
~
FWindowsSystemWideCriticalSection
();
//
检查锁当前是否能够获取,如果能够获取或者前一个拥有着在未释放锁的情况下结束了,返回
true
,否则返回
false
。
bool
IsValid
()
const
;
//
释放锁
void
Release
();
private:
//
防止锁被拷贝和赋值
FWindowsSystemWideCriticalSection
(
const
FWindowsSystemWideCriticalSection
&
);
FWindowsSystemWideCriticalSection
&
operator=
(
const
FWindowsSystemWideCriticalSection
&
);
private:
Windows
::
HANDLE Mutex;
};