windows核心编程-关键段与SRWLock

来源:互联网 发布:手机直播网络要求 编辑:程序博客网 时间:2024/05/19 02:44

线程同步的方式主要有:临界区、互斥区、事件、信号量四种方式。

临界区线程同步也叫关键段线程同步

一、关键段

关键段(Critical Section)是一小段代码,它在执行之前需要独占一些共享资源的访问权。这里的原子方式是代码除了

当前线程之外,没有其他任何线程会同时访问该资源。当然系统仍然可以暂停当前线程去调度其他线程。但是在当

前线程离开关键段之前,系统是不会去调度任何想要访问统一资源的其他线程的。


在使用关键段(CRITICAL_SECTION)时,只有两个必要条件:
1、想要访问资源的线程必须知道用来保护资源的CRITICAL_SECTION对象地址。
CRITICAL_SECTION对象可以作为全局对象来分配,也可以作为局部对象来分配,
或者从堆中动态地分配。
2、如何线程在试图访问被保护的资源之前,必须对CRITICAL_SECTION结构的内部
成员进行初始化。


  1. const int COUNT=10000;
  2. int g_nSum = 0;  
  3. CRITICAL_SECTION g_cs;  
  4.   
  5. DWORD WINAPI FirstThread(PVOID pvParam)  
  6. {  
  7.   EnterCriticalSection(&g_cs);  
  8.   g_nSum = 0;  
  9.   for (int n = 0; n < COUNT; ++n)  
  10.   {  
  11.     g_nSum += n;  
  12.   }  
  13.   LeaveCriticalSection(&g_cs);  
  14.   return g_nSum;  
  15. }  
  16.   
  17. DWORD WINAPI SecondThread(PVOID pvParam)  
  18. {  
  19.   EnterCriticalSection(&g_cs);  
  20.   g_nSum = 0;  
  21.   for (int n = 0; n < COUNT; ++n)  
  22.   {  
  23.     g_nSum += n;  
  24.   }  
  25.   LeaveCriticalSection(&g_cs);  
  26.   return g_nSum;  
  27. }  
我们在代码中定义一个叫g_cs的CRITICAL_SECTION数据结构,然后把任何需要访问共享资源(这里是g_nSum)的代码放在

EnterCriticalSection和LeaveCriticalSection之间。两个函数传入的是g_nSum的地址


关键段:

优点:容易使用,而且在内部也使用了Interlocked函数,所以执行速度很快。

缺点:无法用来在多个进程之间对线程进行同步 哭库玛                             


关键段线程同步常用函数介绍

  1. //1、首先我们要分配一个CRITICAL_SECTION对象,并进行初始化(使用关键段同步的线程必须调用此函数)  
  2. void InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection )  
  3.   
  4. //2、当知道线程将不再需要访问共享资源时,我们应该调用下边的函数来清理CRITICAL_SECTION结构  
  5. void DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection )  
  6.   
  7. //3、在对保护的资源进行访问之前,必须调用下面的函数  
  8. void EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection )  
  9. //可以对上边的函数多次调用,表示调用线程被获准访问的次数  
  10.   
  11. //4、也可以用下边的函数代替EnterCriticalSection  
  12. BOOL TryEnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection )  
  13. //通过返回值判断当前线程是否获准访问资源,线程永远不会进入等待状态,如果  
  14. //返回TRUE表示该线程获准并正在访问资源,离开时必须调用LeaveCriticalSection()  
  15.   
  16. //5、在代码完成对资源的访问后,必须调用以下函数,释放访问权限  
  17. void LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection )  
  18. //转载请注明文章来自:http://blog.csdn.net/windows_nt  

以上访问方式(EnterCriticalSection方式)可能会使调用线程切换到等待状态,这意味着线程必须从用户模式切换到内核模式,这个切换开销非常大。为了提高关键段的性能,Microsoft把旋转锁合并到了关键段中。因此,当调用EnterCriticalSection的时候,它会用一个旋转锁不断地循环,尝试在一段时间内获得对资源的访问权,只有当尝试失败时,线程才会切换到内核模式并进入等待状态。

(如何传入dwSpinCount的值,使效果最佳:通过不断的尝试,一般为4000,参考值)

//1、为了在使用关键段的时候同时使用旋转锁,我们必须调用下面的函数来初始化关键段  BOOL InitializeCriticalSectionAndSpinCount( LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount )
//第一个参数是关键段结构的地址  //第二个参数dwSpinCount表示我们希望旋转锁循环的次数。    //2、我们也可以调用下面的函数来改变关键段的旋转次数  DWORD SetCriticalSectionSpinCount( LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount )  //如果主机只有一个处理器,函数会忽略dwSpinCount参数 


二、Slim读/写锁

SRWLock允许我们区分那些想要读取资源的值的线程(读取者线程)和想要更新资源的值的线程(写入者线程)

//1、首先我们要分配一个SRWLOCK对象并用下边函数初始化它。  void InitializeSRWLock( __out PSRWLOCK SRWLock )    //2、请求对保护资源的独占访问权(写权限)  void AcquireSRWLockExclusive( __inout PSRWLOCK SRWLock )    //3、完成对资源的更新后,应该解除对资源的锁定  void ReleaseSRWLockExclusive( __inout PSRWLOCK SRWLock )    //4、对应的读者线程函数如下  void AcquireSRWLockShared( __inout PSRWLOCK SRWLock )  void ReleaseSRWLockShared( __inout PSRWLOCK SRWLock )  
与关键段相比,PSRWLock缺乏下面两个特性

1、不存在TryEnter(Share/Exclusive)SRWLock之类的函数,如果锁已经被占用,呢么调用会阻塞调用线程

2、不能递归获得PSRWLOCK.也就是说,一个线程不能为了多次写入二多次锁定资源,然后再多次

调用ReleaseSRWLock *释放对资源的锁定


推荐使用SRWLock来代替关键段:性能更优,不仅快,而且允许多个线程同时读取,对那些只需要读取共享资源的

线程来说,提高了吞吐量和可伸缩性。

线程同步性能排序(由高到低)
volatile读取 -->volatile写入-->Interlocked API(原子方式)-->SRWLock-->关键段-->内核对象



0 0
原创粉丝点击