zoukankan      html  css  js  c++  java
  • 秒杀多线程第五篇 经典线程同步 关键段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.推荐关键段与旋转锁配合使用

  • 相关阅读:
    Mysql 之导入导出
    Go gin之文件上传
    记录Go gin集成发送邮件接口的坑
    关于mysql某个用户无法登陆的情况
    面向对象程序设计的分析基本步骤
    提示框判断事件
    事件响应的公共方法
    IComparable<T>.CompareTo(T) 方法
    浏览器缓存机制
    PHP中include和require
  • 原文地址:https://www.cnblogs.com/yuqilihualuo/p/3023970.html
Copyright © 2011-2022 走看看