秒杀多线程第五篇 经典线程同步 关键段CS .

参考博客:http://blog.csdn.net/morewindows/article/details/7442639

本篇用CRITICAL_SECTION来解决多线程同步互斥的问题

CRITICAL_SECTION 一共有四个函数:

1.初始化:定义关键段变量后必须先进行初始化才能使用

void InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection)

2.销毁:使用完之后要记得销毁

 

void DeleteCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

3.进入关键区域:保证各个线程互斥的进入关键区域

 

void EnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

4.离开关键区域:

void LeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

 

 1 #include <stdio.h>

 2 #include <process.h>

 3 #include <windows.h>

 4 //关键段的定义

 5 CRITICAL_SECTION  g_csThreadParameter, g_csThreadCode; 

 6 long g_num; //登录次数

 7 unsigned int __stdcall Fun(void *pPM); //线程函数

 8 const DWORD THREAD_NUM = 10;//启动线程数

 9 unsigned int __stdcall Fun(void *pPM)  

10 {  

11 //由于创建线程是要一定的开销的,所以新线程并不能第一时间执行到这来   

12     int nThreadNum = *(int *)pPM; //子线程获取参数   

13     LeaveCriticalSection(&g_csThreadParameter);

14     Sleep(50);//some work should to do  

15     EnterCriticalSection(&g_csThreadCode);//进入子线程序号的关键区域

16     g_num++;  //处理全局资源  

17 //    InterlockedIncrement(&g_num);

18     Sleep(0);//some work should to do   

19     printf("线程编号为%d  全局资源值为%d\n", nThreadNum, g_num);  

20     LeaveCriticalSection(&g_csThreadCode);

21     return 0;  

22 } 

23 int main()

24 {

25     printf("     关键段CS的演示\n");

26     g_num = 0;

27     

28     //关键段的初始化

29     InitializeCriticalSection(&g_csThreadParameter);

30     InitializeCriticalSection(&g_csThreadCode);

31     HANDLE  handle[THREAD_NUM];

32     int i=0;

33     while (i<THREAD_NUM)

34     {

35         EnterCriticalSection(&g_csThreadParameter);//进入子线程序号的关键区域

36         handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);  

37         i++;

38     }

39     

40     WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);

41     //离开关键区域

42     LeaveCriticalSection(&g_csThreadParameter);

43     LeaveCriticalSection(&g_csThreadCode);

44     return 0;

45 }

运行结果:秒杀多线程第五篇 经典线程同步 关键段CS .

 结果可以看出子线程已经互斥访问资源 ,但子线程间的同步还是出现了问题

下面分析为什么不能实现线程间的同步

关键段结构体CRITICAL_SECTION的定义:

typedef struct _RTL_CRITICAL_SECTION {

    PRTL_CRITICAL_SECTION_DEBUGDebugInfo;

    LONGLockCount;

    LONGRecursionCount;

    HANDLEOwningThread; // from the thread's ClientId->UniqueThread

    HANDLELockSemaphore;

    DWORDSpinCount;

} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;

 第一个参数:调试时使用

第二个参数:初始化为-1,表示有多少个线程在等待

第三个参数:表示该关键段的拥有者获得该关键段的次数,初始化为0

第四个参数:拥有该关键段的线程句柄

第五个参数:是一个自复位事件

第六个参数:旋转锁的设置,单核CPU无意义

 

关键段会有线程拥有权的概念第四个参数OwningThread来批准进入关键段的线程

EnterCriticalSection()会更新第四个参数,并立即返回让该线程进入其他线程再次调用EnterCriticalSection()则会被切换到等待状态

一旦拥有所有权的线程调用LeaveCriticalSection()使其进入次数为零时,系统会自动更新关键段,并将等待中的线程切换到可调度状态

所以关键段用于进程间的互斥,但不能用于进程间的同步

 

由于线程切换到等待状态开销较大,为了提高性能,微软将旋转锁合并到关键段中:EnterCriticalSection()会先用一个旋转锁不断循环,尝试一段时间后

再将线程进入循环状态

1.函数InitializeCriticalSectionAndSpinCount()初始化关键段,并设置旋转锁次数,一般设置为4000次

 

BOOLInitializeCriticalSectionAndSpinCount(

 

  LPCRITICAL_SECTIONlpCriticalSection,

 

  DWORDdwSpinCount);

2.修改关键段的旋转次数SetCriticalSectionSpinCount( )

 

DWORDSetCriticalSectionSpinCount(

 

  LPCRITICAL_SECTIONlpCriticalSection,

 

  DWORDdwSpinCount);

 

 

 

Windows核心编程》第五版的第八章推荐在使用关键段的时候同时使用旋转锁,这样有助于提高性能。值得注意的是如果主机只有一个处理器,那么设置旋转锁是无效的。无法进入关键区域的线程总会被系统将其切换到等待状态。

 

 

最后总结下关键段:

1.关键段共初始化化、销毁、进入和离开关键区域四个函数。

2.关键段可以解决线程的互斥问题,但因为具有“线程所有权”,所以无法解决同步问题。

3.推荐关键段与旋转锁配合使用

你可能感兴趣的:(线程同步)