使用临界区对象(CriticalSection)需要注意的一些事情

小编 2026-06-22 阅读:1131 评论:0
使用临界区对象(CriticalSection)需要注意的一些事情 2013年10月28日 ⁄ 综合 ⁄ 共 2591字 ⁄ 字号 小 中 大 ⁄ 评论关闭 1. 临界区对象不是内核对象,因此不能继承,...

使用临界区对象(CriticalSection)需要注意的一些事情

2013年10月28日 ⁄ 综合 ⁄ 共 2591字 ⁄ 字号 ⁄ 评论关闭

1. 临界区对象不是内核对象,因此不能继承,不能跨进程,也不能用waitfor什么的函数来限定时间等待。这个很好理解,你想想WaitFor要求传一个句柄,而临界区对象的类型都不是句柄,也不能用CloseHandle来关闭,怎么可能会能让WaitForXXX搞了。

2. 临界区对象使用前必须初始化,不初始化会崩溃,这是我的亲历。

3. 线程进入临界区之前会先自旋那么几次,所有自旋锁都失败了之后会创建一个内核对象然后等待这个内核从而进入到内核模式。

4. Enter和Leave必须匹配,每Enter一次,就得Leave一次,这又是可恶的计数机制。参见下面的代码:

 

typedef struct _RTL_CRITICAL_SECTION {
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;

    //
    //  The following three fields control entering and exiting the critical
    //  section for the resource
    //

    LONG LockCount;
    LONG RecursionCount;
    HANDLE OwningThread;        // from the thread\'s ClientId->UniqueThread
    HANDLE LockSemaphore;
    ULONG_PTR SpinCount;        // force size on 64-bit systems when packed
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;

这是临界区对象的定义,看见

RecursionCount

这个对象了吧,你觉得它能干点啥?同时在这里你还能看到一个信号量的内核对象,还有一个自旋数量。这些玩意印证了上面的话。如果你同一个线程Leave之前Enter了两次,必须调用两个Leave,不然这个临界区对象依然会阻塞别的线程。再不明白就去看我前面有关挂起线程的那个博文。

 

5. 由于进入临界去是无限等待的,因此你有时间肯定希望有种方法能够查看一下临界区是否可用,不可用则希望线程立刻去做其它的事情。这时候,你就需要一个TryEnterCriticalSectionAPI,这玩意很好理解,你踹一脚临界区,如果能进去就进去,不能进去这个API立刻以False返回,你就可以安排线程去做其它的事情。注意:你一脚踹进去了之后完事了记得要离开(LeaveCriticalSection)。

 

6. 前面说了,临界区真正用内核对象挂起线程之前会自旋好几次,因此你看对象里就有一个自旋锁的计数。你可以改这个自旋锁的数量。当然我不是说让你直接修改对象的成员变量!你可以在初始化的时候指定自旋锁的数量,用这个API:InitializeCriticalSectionAndSpinCount。在这里小说一下临界区为什么会自旋。因为程序从用户态转到内核模式需要昂贵的开销(大概数百个CPU周期),很多情况下,A线程还没完成从用户态转到内核态的操作呢,B线程就已经释放资源了。于是临界区就先隔一段时间自旋一次,直到所有自旋次数都耗尽,就创建个内核对象然后挂起线程。但是,如果您的机器只有一个CPU,那么这个自旋次数就没用了,操作系统直接会无视它。原因如下:你自旋着呢,操作B线程释放不了资源,于是你还不如直接切入等待状态让B来释放资源。动态更改自旋数量请使用SetCriticalSectionSpinCount,别做直接更改对象成员变量的二事!

 

7. 最后,初始化临界区和进入临界区的时候都有可能会遇到异常状况,比如初始化的时候需要申请一些内存来保存DEBUG的信息(参见上面代码的第一个成员变量),如果内存不够,初始化就崩了。进入临界区的时候可能需要新建一个内核对象,如果内存不够,也崩了。解决这个问题的方法有两种

  1. 结构化异常处理
  2. 初始化的时候使用InitializeCriticalSectionAndSpinCount。这个API有返回值,如果它申请DEBUG信息失败,返回FALSE,另外,刚才提到了这个API可以指定自旋锁的自旋次数。这个自旋次数的范围并非是用0到0xFFFF
    FFFF而是0--->0x00FF FFFF,因此你可以设定高位为1指示初始化的时候直接建立内核对象。如果建立失败,这个函数也会调用失败。当然了,一上来就搞一个内核对象似乎有点浪费内存,但是这样能够保证进入临界区不会失败!但是吧,你需要注意,设置高位来保证内核对象的创建只能在2000上玩。MSDN上有说明,不信你看:
  3. Windows  2000:  If the high-order bit is set, the function pre-allocates the event used by theEnterCriticalSection
    function. Pre-allocation guarantees that entering or leaving the critical section will not raise an exception in low memory conditions. Do not set this bit if you are creating a large number of critical section objects, because it consumes a significant amount
    of nonpaged pool. Note that this event is allocated on demand starting with Windows XP and the high-order bit is ignored.

最后是一些实验:

我们看看用InitializeCriticalSection初始化一个临界区对象后,这些成员变量(除去DEBUG外)都是什么样子。

 

我们Enter一下,看看会变成什么样子

 

我们再让其它线程也Enter一下看看

 

可见,新建了一个内核对象。

我们现在让主线程退出临界区

 

对照线程句柄我们可以看出第二个线程获得了临界区对象。

我们再让第二个线程退出临界区。

 

临界区除去内核对象外回到了原始状态。

 

实验2:我们让临界区对象在同一线程内相继被进入两次

 

::EnterCriticalSection(&g_cs);
::EnterCriticalSection(&g_cs);

 

可见,计数增加了一个,变成了,因此你得leave两次才能开锁

转自:https://www.xuebuyuan.com/1304408.html

版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表