您的位置:首页 > 其它

windows critical section (关键段)

2011-06-30 23:57 197 查看
不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它进行访问。每个线程中访问临界资源的那段代码称为临界区(Critical Section)。

  每个线程中访问临界资源的那段程序称为临界区(Critical Section)(临界资源是一次仅允许一个线程使用的共享资源)。每次只准许一个线程进入临界区,进入后不允许其他线程进入。不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它进行访问。

  多个线程中涉及到同一个临界资源的临界区称为相关临界区。

  线程进入临界区的调度原则是: ①如果有若干线程要求进入空闲的临界区,一次仅允许一个线程进入。②任何时候,处于临界区内的线程不可多于一个。如已有线程进入自己的临界区,则其它所有试图进入临界区的线程必须等待。③进入临界区的线程要在有限时间内退出,以便其它线程能及时进入自己的临界区。④如果线程不能进入自己的临界区,则应让出CPU,避免线程出现“忙等”现象。

  如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。

  临界区在使用时以CRITICAL_SECTION结构对象保护共享资源,并分别用EnterCriticalSection()和LeaveCriticalSection()函数去标识和释放一个临界区。所用到的CRITICAL_SECTION结构对象必须经过InitializeCriticalSection()的初始化后才能使用,而且必须确保所有线程中的任何试图访问此共享资源的代码都处在此临界区的保护之下。否则临界区将不会起到应有的作用,共享资源依然有被破坏的可 能。

下面看看代码吧....

// critical section.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <windows.h>
#include <iostream>
using namespace std;
DWORD WINAPI FirstThread(PVOID pvParam);
DWORD WINAPI SecondThread(PVOID pvParam);
const long Count = 10000;
long  g_nSum = 0;
CRITICAL_SECTION g_cs; //定义关键段结构
int _tmain(int argc, _TCHAR* argv[])
{
if(InitializeCriticalSectionAndSpinCount(&g_cs,4000) != TRUE) //初始化关键段结构,同时使用旋转锁。
{
cout<<"初始化失败"<<endl;
return 1;
}
HANDLE hThread1 = CreateThread(NULL,0,FirstThread,NULL,0,0);
HANDLE hThread2 = CreateThread(NULL,0,SecondThread,NULL,0,0);
CloseHandle(hThread1);
CloseHandle(hThread2);
Sleep(5000);
DeleteCriticalSection(&g_cs); //释放 关键段
system("pause");
return 0;
}
DWORD WINAPI FirstThread(PVOID pvParam)
{
//需要使用共享资源的代码,放在EnterCriticalSection(&g_cs)和LeaveCriticalSection(&g_cs)之间
EnterCriticalSection(&g_cs);
g_nSum = 0;
for(int i = 0;i<1000;++i)
{
g_nSum = 0;
for(int n=1; n<=Count; ++n)
{
g_nSum += n;
}
}
LeaveCriticalSection(&g_cs);
cout<<"First:"<<g_nSum<<endl;
return 0;
}
DWORD WINAPI SecondThread(PVOID pvParam)
{
EnterCriticalSection(&g_cs);
g_nSum = 0;
for(int i = 0;i<1000;++i)
{
g_nSum = 0;
for(int n=1; n<=Count; ++n)
{
g_nSum += n;
}
}
LeaveCriticalSection(&g_cs);
cout<<"Second:"<<g_nSum<<endl;
return 0;
}


如果不使用critical section机制,所输出的内容将会是不可预料的,但是 这种方法也有一个缺点:无法用来在多个进程之间对线程进行同步。

补充:

InitializeCriticalSectionAndSpinCount(&g_cs,4000) 的解释,

当线程试图进入一个关键段,但这个关键段正在被另一个线程占用的时候,函数会立即把调用线程切换到等待状态。这意味线程必须从用户模式切换到内核模式(大约1000个CPU周期),这个切换的开销非常大。在多核处理器的机器上,当前占用资源的线程可能在另一个处理器上运行,而且可能很快就会结束对资源的访问。事实上,在需要等待的线程完全切换到内核模式之前,占用资源的线程可能已经释放了资源。这种情况下,会浪费大量的cpu时间

为了提高关键段的性能,Microsof把旋转锁合并到了关键段中。因此,当调用EnterCriticalSection()的时候,它会用一个旋转锁不断的循环,尝试在一段时间内获得对源的访问权。只有当尝试失败的时候,线程才会切换到内核模式并进入等待状态。

(总结起来就是 用 小代价 去替换 大代价,这里的思路有点像 c++ 中vector容器的内存分配原则哦!~)

如果在单处理器计算机上运行时调用该函数,dwspincount参数将被忽略,它的计数始终被置为0。这是对的,因为在单处理器计算机上设置循环次数是毫无用处的,如果另一个线程正在循环运行,那么拥有资源的线程就不能放弃它。

----《windows核心编程》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: