Win32多线程程序设计(四)

线程间的同步控制

前言:

撰写多线程程序的一个最具挑战性的问题是:如何让一个线程和另一个线程合作,即如何进行进程和线程的协调工作,线程间的协调工作是由同步机制完成的,同步机制相当于线程之间的红绿灯,我们可以设计让一组线程使用同一个红绿灯系统,这个红绿灯系统必须达到如下两个目的:

1)    这个红绿灯系统负责给某个线程绿灯而给其他线程红灯

2)    这组红绿灯系统必须确保每一个线程都有机会获得绿灯

有很多种同步机制可以运用,使用哪一种同步机制完全视欲解决的问题而定,我们这节讨论每一种同步机制的使用方法,并对每一种同步机制,分析”为什么使用”及”何时使用”

Critical Sections(关键区/临界区)

Critical section是Win32中比较容易使用的一个同步机制,它是WINNT.H定义的一种结构体类型的变量,并不是核心对象,存在于进程的内存空间中,适用范围是单一进程的各线程之间,不能用于同步不同进程间的线程,下面我们对Critical section同步的机制及过程进行分析

1.CRITICAL_SECTION简介

  临界区是一种轻量级机制,在某一时间只允许一个线程执行某个给定代码段,通常在修改全局数据时使用临界区,事件,信号量也用于多线程同步,但临界区与他们不同,它不总是执行向内核模式转换,速度非常快,但它的缺点在于临界区只能用于对同一进程内的线程进行同步

使用临界区的过程:

  在将临界区传递给InitializeCriticalSection时,临界区开始存在,初始化之后,代码即将临界区传递给EnterCriticalSection和LeaveCriticalSection API,当一个线程自EnterCriticalSection中返回后,所有其他调用EnterCriticalSection的线程都将被阻止,知道该线程调用LeaveCriticalSection为止,最后,当不需要临界区时,一种良好的编码习惯是将其传递给DeleteCriticalSection

2.使用临界区时用到的API

1) VOID InitalizeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

  功能:初始化一个critical section对象

  参数:lpCriticalSection 一个指针,指向欲被初始化的CRITICAL_SECTION对象,这个变量应该事先在程序中定义

  返回:VOID

 

 2) VOID DeleteCriticalSection(LPCRITICAL_SECTION  lpCriticalSecion);

  参数:lpCriticalSeciton 指向一个不再需要的CRITICAL_SECTION变量

  返回:返回VOID

 

3) VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

  参数:lpCriticalSection  指向一个你即将锁定的CRITICAL_SECTION变量

  返回值:返回VOID

 

4) VOID LeaveCriticalSection(LPCRITICAL_SECTION  lpCriticalSection);

  参数:lpCriticalSection  指向一个即将解除锁定的CRITICAL_SECTION变量

  返回值:返回VOID

 

3.临界区进阶理解

  很多人对CRITICAL_SECTION的理解是错误的,认为CRITICAL_SECTION锁定了资源,其实CRITICAL_SECTION是不能够锁定资源的,它是通过修改自身的状态来来达到线程间的同步的,简单的说,当一个线程执行了EnterCriticalSection之后,cs的状态被修改了,此时没有任何资源被锁定,不管什么资源,其他线程还是可以访问的,只不过,在这个线程尚未执行LeaveCriticalSection()之前,其它线程碰到EnterCriticalSection语句的话,就会处于等待状态,即CRITICAL_SECTION通过修改自身的状态实现同步共享资源的效果,所以,使用中我们必须把每一个线程中访问共享资源的语句放在EnterCriticalSection和LeaveCriticalSection之间。

错误的程序段:

第一个线程函数:
DWORD   WINAPI   ThreadFuncA(LPVOID   lp)
{
            EnterCriticalSection(&cs);
            ...
            //   操作共享资源
            ...
            LeaveCriticalSection(&cs);
            return   0;
}

第二个线程函数:

DWORD   WINAPI   ThreadFuncB(LPVOID   lp)
{
            ...
            //   操作共享资源
            ...
            return   0;
}

这里,当第一个线程访问共享资源时,第二个线程因为其未被阻塞,所以依然可以访问共享资源

正确代码:

我们需要在第二个线程访问共享资源的前后加上EnterCriticalSection(&cs)和LeaveCriticalSection(&cs),如下:

DWORD   WINAPI   ThreadFuncA(LPVOID   lp)
{
            EnterCriticalSection(&cs);
            ...
            //   操作共享资源
            ...
            LeaveCriticalSection(&cs);
            return   0;
}

 

  我们要理解,CRITICAL_SECTION不是针对资源的,而是针对不同线程间的代码段的,它通过设置自身状态,来达到同步访问共享资源的目的,使用时,我们在进程范围内,定义一个全局变量CRITICAL_SECTION,在任何访问共享资源的地方加入EnterCriticalSection和LeaveCriticalSection语句,使得同一时间只能够有一个线程的代码段访问到共享资源。

 4.CRITICAL_SECTION使用总结

1)      不是核心对象,使用时不需要进入操作系统核心模式,直接在"user mode"下就可以进行操作,同步时执行时间更快

2)      只能用于同一进程的多个线程同步访问共享变量,不能同步不同进程的线程

3)      使用时要将其定义为CRITICAL_SECTION类型的全局变量,不能定义为主线程的局部变量,因为这样会导致其他线程中该变量未定义

4)      它是通过修改自身的状态达到多个线程同步的目的,而不是针对锁定共享资源达到同步目的

5)      在需要访问共享资源的所有线程的代码前后加上EnterCriticalSeciont()及LeaveCriticalSection()

转载于:https://www.cnblogs.com/liuhao2638/archive/2012/11/14/2770152.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值