临界区指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个来自线程访问的特性。当有线程进入临界区段时,其他线程或是进程必须等待(例它如:bounded waiting 等待法),有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共用资源是被互斥360百科获得使用,例如:semaphore。只能被单一线程访问宗饭的设备,例如:打印机。
- 中文名 临界区
- 外文名 Critical section
- 所属范围 计算机程序
基本简介
每个进程中访问临界资源的那段代码称为临界区(Cri相种影呼历tical Section)(临界资源是一次仅允许一个进程使用的共享资源)。每次只准许一个进程进入临界节刑害已换晶分态区,进入后不允许其他进程进入。不论是硬件临界资源,还是软件临来自界资源,多个进程必须互斥360百科地对它进行访问。
多个进程中涉及到同一个临界资源的临界区称为相关临界压移喜王标织维区。.
调度法则
进程进入临界区的调度原则是:
1曾曾继似、如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入。
2、任何时候措以镇历,处于临界区内的进程不可多于一个。如已有进程进入自己的临界区,则其它所有试图进入临界区的进程必须等待企袁道温永他取坐各坐毛。
3、进入临界区的进程卫要在有限时间内退出,以便其它进程能及时进入自己的临界区。
4、如考类伟果进程不能进入自己的临界区,则应让出CPU,避免进程出现"忙等"现象。
同步问题
须受帮食英代扬化日 有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入补拉临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。
临界区在使用时以CRITI己领织可必功CAL_SECTION结构对象保护共享资源,并分紧耐别用EnterCriticalSection()和Lea来自veCriticalSection()函数去标识和释放一个临界区。所用到的CRITICAL_SECTION结构对象必须经过InitializeCriticalSection()的初始化后才能使用,而且必须确保所有线程中的任何试图访问此共享资源的代码都处在此临界区的保护之下。否则临界区将不会起到应有的作用,共享资源依然有360百科被破坏的可能。
下面通过一段代码展示了临界区在保护多线程访问的共享资源中的作用。通过两个线程来分别对全局变量g_cArray[10]进行写入操作,用临界区结构对象g_cs来保持线程的同步,并在开启染划今够决又道尼农线程前对其进行初始化。为了使实验效果更加明显,体现出临界区的作用,在线程函数对共享资源g_cArray[10]的写入时,以Sleep()函数延迟1毫秒,使其他线程同其抢占CPU的可能性增大。如果不使用临界区对其进行保护,则共享资源数据将被破坏(参见图1(a)所示计算结果福控跟革训书),而使用临界区对线程保持同步后则可以得色占视药到正确的结果(参见图1(b)所示计算结果)。
代码实现清单附下:
图一存在问题
在使用临界区时,一般不允许其运行时间过长,只展减审队众激将本块装要进入临界区的线程还没有离开,其他所有试图进入此临界区的线程都会被孙挂起而进入到等待状态,并会在一定程度上影响程序的运行性能。尤其需要注意的是不要将等待用户输入或是其他一些外界干预的操作包含到临界区。如果进入了临界区老保电足硫源样深晚粉行却一直没有释放,同样也会引起其他线程振的长时间等待。换句话说,在执行了EnterCriti内陆calSection载业影都待左角飞约改()语句进入临界区后无论发生什么,必须确保与之匹配的LeaveCriticalSection()都能够被执行到。可以通过添加结构化异常处理代扬排肉半二雷李温投状码来确保LeaveCriticalSection()语句的执行。虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。
1、 笑所乎刻设愿细临界区的退出,不会检测是否是已经进入的线程,也就是说,我可以在A线程中调用进入临界区函数,在B线程调用退出临界区的函数,同样是成功;
那争批定歌校看向升无指2、 我在测试临界区的集对溶时候,如果我没有调用进入临界区的函数,直接退出的话,系统经载没有进行判断,但是计数发现了改变,此时此临界区就再也用不了了,因为结构中的数据已经乱掉了。
解决方法如下:
ypedef class mutex_lock
{
public:
mutex_lock()
: LockCount(-1)
, hEvent(0)
{
}
~mutex_lock()
{
if(NULL != this->hEvent)
{
CloseHandle(this->hEvent);
}
this->hEvent = NULL;
this->LockCount = -1;
}
long GetLock();
long ReleaseLock();
private:
mutex_lock(mutex_lock&);
long LockCount;
HANDLE hEvent;
} MUTEXLOCK, *LPMUTEXLOCK;
long mutex_lock::GetLock()
{
__asm
{
movebx, [this];//把基址保存在ebx中
lock incdword ptr [ebx];//对LockCount进行加锁加,保证多CPU时的唯一性,
jeLRET1;//如果LockCount加1为0的话,表示没有人在使用资源,同时利用上面的互斥加对资源进行占用。
cmpdword ptr [ebx+4], 0;//此时LockCount加1大于0的情况,表示已有人使用此资源,要对此资源进行加内核锁
jneL1;//如果平常没有创建内核锁,则进行创始一个
push 0;
push 0;
push 0;
push 0;
call dword ptr [CreateEvent];
movedx, eax;//创建内存锁成功后,在同样对hEvent变量进行互斥比较后替换,以处理多个线程同时对此值进行替换的情况,保证只有一个能够成功替换
moveax, 0;
lock cmpxchg dword ptr [ebx+4], edx; //互斥比较替换
jeL1;
push edx;//如果已经被别人替换过了,需要把自己创建的内核锁释放
call dword ptr [CloseHandle];
L1:
push INFINITE;
push [ebx+4];
call dword ptr [WaitForSingleObject]; // 在内核级进行等待
LRET1:
moveax, 0
}
}
long mutex_lock::ReleaseLock()
{
__asm
{
movebx, [this];//把基址保存在ebx中
moveax, -1;
lock xadd dword ptr [ebx], eax; //进行交换自减交换操作,运行后,eax中是第一操作数先前的值,此值用会返回用来判断是否多调用了ReleaseLock
jlLRET2;//没有别的线程占用资源,直接返回,用户可以通过返回值,分析是否失败
cmpdword ptr [ebx+4], 0;//有别的线程在等待,检查内核锁,如果没有则进行创建
jneL2;
push 0;
push 0;
push 0;
push 0;
call dword ptr [CreateEvent];
movedx, eax;
moveax, 0;
lock cmpxchg dword ptr [ebx+4], edx; //互斥替换内核锁句柄
jeL2;//已经有内核锁了,把自已申请的关闭
push edx;
call dword ptr [CloseHandle];
L2:
push [ebx+4];//设计信号,唤醒一个线程
call dword ptr [SetEvent];
moveax, 0;
LRET2:
}
}