Windows 临界区(CRITICAL_SECTION)的使用

由于需要在多线程中并发操作临界数据,为了保证临界数据操作的完整性,Linux下使用锁(Linux下锁可以看我的这篇博客Linux 互斥锁 递归锁 自旋锁 读写锁),而在Windows下,使用的是临界区。

每个线程中访问临界资源的那段程序称为临界区(Critical Section)(临界资源是一次仅允许一个线程使用的共享资源)。每次只准许一个线程进入临界区,进入后不允许其他线程进入。不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它进行访问。在说临界区之前,我们先讲下同步和互斥,以理解为什么需要临界区。

 


同步和互斥机制

  • 基本概念

同步和互斥的概念有时候很容易混淆,可以简单地认为同步是更加宏观角度的一种说法,互斥是冲突解决的细节方法。所谓同步就是调度者让任务按照约定的合理的顺序进行,但是当任务之间出现资源竞争,也就是竞态冲突时,使用互斥的规则强制约束允许数量的任务占用资源,从而解决各个竞争状态,实现任务的合理运行。

同步和互斥密不可分,有资料说互斥是一种特殊的同步,对此我不太理解,不过实际中想明白细节就行,文字游戏没有意义。

简单来说:

  • 同步与互斥机制是用于控制多个任务对某些特定资源的访问策略
  • 同步是控制多个任务按照一定的规则或顺序访问某些共享资源
  • 互斥是控制某些共享资源在任意时刻只能允许规定数量的任务访问
  • 角色分类

整个协调流程涉及的角色本质上只有三类:

  • 不可独占的共享资源
  • 多个使用者
  • 调度者

调度者需要为多个运行任务制定访问使用规则来实现稳定运行,这个调度者可以是内核、可以是应用程序,具体场景具体分析。

  • 重要术语

要很好地理解同步和互斥,就必须得搞清楚几个重要术语:

  • 竞争冒险(race hazard)或竞态条件(race condition)

最早听说这个术语是在模电数电的课程上,门电路出现竞态条件造成错误的结果,在计算机里面就是多个使用者同时操作共享的变量造成结果的不确定。

  • 临界区

临界区域critical section是指多使用者可能同时共同操作的那部分代码,比如自加自减操作,多个线程处理时就需要对自加自减进行保护,这段代码就是临界区域。

 


 临界区(Critical Section)

Linux下有递归锁,递归锁是同一个线程在不解锁的情况下,可以多次获取锁定同一个递归锁,而且不会产生死锁。windows下的互斥量和临界区(关键段)默认支持递归锁。

  • void InitializeCriticalSection(  LPCRITICAL_SECTION lpCriticalSection);初始化一个临界区对象
  • void DeleteCriticalSection(_Inout_ LPCRITICAL_SECTION lpCriticalSection);删除临界区对象释放由该对象使用的所有系统资源

  • void EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection );//进入临界区,相当于Linux下lock

  • void LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection );//删除临界区,相当于Linux下unlock

#include <Windows.h>
#include <iostream>
#include <string>
​
int counter = 0;
​
// 定义一个临界区变量
CRITICAL_SECTION g_cs;
​
void doit(void* arg)
{
    int i, val;
    for (i=0; i<5000; i++)
    {
        // 临界区默认支持递归锁,所以这里在临界区里再次进入临界区也没关系,正常使用调用一次即可
        EnterCriticalSection(&g_cs);
        EnterCriticalSection(&g_cs);
​
        val = counter;
        printf("thread %d : %d\n", int(arg), val+1);
        counter = val + 1;
​
        // 离开临界区
        LeaveCriticalSection(&g_cs);
        LeaveCriticalSection(&g_cs);
    }
}
​
int main(int argc, char*argv[])
{
        // 初始化临界区
        InitializeCriticalSection(&g_cs);
​
        HANDLE hThread1 = CreateThread(NULL,0, (LPTHREAD_START_ROUTINE)doit, (void*)1, 0, NULL);
        HANDLE hTrehad2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)doit, (void*)2, 0, NULL);
​
        WaitForSingleObject(hThread1, INFINITE);
        WaitForSingleObject(hTrehad2, INFINITE);
​
        // 删除临界区
        DeleteCriticalSection(&g_cs);
​
    
    return 0;
}

使用临界区加1次锁和2次锁,均可以正确的输出1~10000。

 

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值