秒杀多线程第五篇 经典线程同步 关键段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 }

运行结果:

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

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

关键段结构体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.推荐关键段与旋转锁配合使用

原文地址:https://www.cnblogs.com/yuqilihualuo/p/3023970.html