一、问题回顾

我们上一篇文章最后的程序的输出 g_Count 的值不是每次都正确,原因是没有对全局资源 g_Count 进行互斥访问(就是同一时刻只能由一个线程访问),接下来我们就来说一下使用关键段来给全局资源加锁以实现互斥访问。

这是上一篇中的程序:


  1. #include <stdio.h>
  2. #include <windows.h>
  3. const unsigned int THREAD_NUM = 50;
  4. unsigned int g_Count = 0;
  5. DWORD WINAPI ThreadFunc(LPVOID);
  6. int main()
  7. {
  8. HANDLE hThread[THREAD_NUM];
  9. for (int i = 0; i < THREAD_NUM; i++)
  10. {
  11. hThread[i] = CreateThread(NULL, 0, ThreadFunc, 0, 0, NULL); // 创建线程
  12. }
  13. WaitForMultipleObjects(THREAD_NUM, hThread, true, INFINITE); //一直等待,直到所有子线程全部返回
  14. printf(" 总共 %d 个线程给 g_Count 的值加一,现在 g_Count = %d\n", THREAD_NUM, g_Count);
  15. return 0;
  16. }
  17. DWORD WINAPI ThreadFunc(LPVOID p)
  18. {
  19. Sleep(50);
  20. g_Count++;
  21. Sleep(50);
  22. return 0;
  23. }

二、 关键段 CriticalSection 声明及相关函数

(一)CriticalSection 声明

  1. CRITICAL_SECTION 关键段名字; // eg: CRITICAL_SECTION cs;

CRITICAL_SECTION 结构说明:在 vs 中 先声明一个 关键段, 鼠标放到 CRITICAL_SECTION 关键字上按 F12 转到定义如下:

  1. typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;

再把鼠标放到 RTL_CRITICAL_SECTION 上按 F12 即可转到 CRITICAL_SECTION 结构体的定义 如下:

  1. typedef struct _RTL_CRITICAL_SECTION {
  2. PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
  3. //
  4. // The following three fields control entering and exiting the critical
  5. // section for the resource
  6. //
  7. LONG LockCount;
  8. LONG RecursionCount;
  9. HANDLE OwningThread; // from the thread's ClientId->UniqueThread
  10. HANDLE LockSemaphore;
  11. ULONG_PTR SpinCount; // force size on 64-bit systems when packed
  12. } RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
  • 第一个参数:PRTL_CRITICAL_SECTION_DEBUG DebugInfo; 调试的时候用的,先不做介绍。

  • 第二个参数:LONG LockCount; 初始化为-1,n表示有n个线程在等待。

  • 第三个参数:LONG RecursionCount; 表示该关键段的拥有线程对此资源获得关键段次数,初为0。

  • 第四个参数:HANDLE OwningThread; 即拥有该关键段的线程句柄

  • 第五个参数:HANDLE LockSemaphore; 实际上是一个自复位事件。

  • 第六个参数:ULONG_PTR SpinCount; 旋转锁的设置,用于多处理器。

(二)CriticalSection相关函数

1.函数功能:初始化,定义关键段变量后必须先初始化。

  1. void InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

2.函数功能:销毁,用完之后记得销毁。

  1. void DeleteCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

3.函数功能:进入关键区域,系统保证各线程互斥的进入关键区域。

  1. void EnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

4.函数功能:离开关关键区域

  1. void LeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

三、实例

现在使用关键段来解决上面的问题,代码如下:


  1. #include <stdio.h>
  2. #include <windows.h>
  3. const unsigned int THREAD_NUM = 50;
  4. unsigned int g_Count = 0;
  5. CRITICAL_SECTION cs; //声明关键段
  6. DWORD WINAPI ThreadFunc(LPVOID);
  7. int main()
  8. {
  9. InitializeCriticalSection(&cs); // 初始化关键段
  10. HANDLE hThread[THREAD_NUM];
  11. for (int i = 0; i < THREAD_NUM; i++)
  12. {
  13. hThread[i] = CreateThread(NULL, 0, ThreadFunc, 0, 0, NULL); // 创建线程
  14. }
  15. WaitForMultipleObjects(THREAD_NUM, hThread, true, INFINITE); //一直等待,直到所有子线程全部返回
  16. printf(" 总共 %d 个线程给 g_Count 的值加一,现在 g_Count = %d\n", THREAD_NUM, g_Count);
  17. DeleteCriticalSection(&cs); //销毁关键段
  18. return 0;
  19. }
  20. DWORD WINAPI ThreadFunc(LPVOID p)
  21. {
  22. Sleep(50);
  23. EnterCriticalSection(&cs); // 进入关键段
  24. g_Count++;
  25. LeaveCriticalSection(&cs); // 离开关键段
  26. Sleep(50);
  27. return 0;
  28. }

运行结果如下图所示,给全局资源 g_Count 加锁,实现互斥访问,就能够让每个线程正确给 g_Count 值加一 :

windows多线程(四) 关键段 CriticalSection的更多相关文章

  1. Windows多线程中关键段(Critical Section)的应用

    先看如下代码:(用Visual Studio 2010按照Win32 Console程序创建向导创建) #include "stdafx.h" #include <proce ...

  2. windows多线程(六) 互斥量Mutex与关键段CriticalSection比较

    一.关键段CS 和 互斥量Mutex 的相同点:都有线程拥有权 关键段和互斥量都有线程拥有权,即可以被一个线程拥有.在 前面讲关键段CS的文章中有说到,关键段结构体的第四个参数保存着拥有该关键段的线程 ...

  3. windows多线程(八) 信号量Semaphore

    如果你看到了这里,我就认为你已经对掌握了有关关键段 CriticalSection.互斥量Mutex和事件Event有关的内容,所以最基本的东西就不再介绍了.如果没有掌握上面说的内容,可以看这里: 关 ...

  4. windows多线程(七) 事件event

    前面说的互斥量Mutex与关键段CriticalSection都不能实现线程的同步,只能实现互斥,接下来我们用时间event就可以实现线程的同步了,事件也是一个内核对象. 一.相关函数说明 (一) 创 ...

  5. [一个经典的多线程同步问题]解决方案一:关键段CS

    前面提出了一个经典的多线程同步互斥问题,本篇将用关键段CRITICAL_SECTION来尝试解决这个问题. 本文先介绍如何使用关键段,然后再深层次的分析下关键段的实现机制和原理. 关键段CRITICA ...

  6. 多线程面试题系列(5):经典线程同步 关键段CS

    上一篇提出了一个经典的多线程同步互斥问题,本篇将用关键段CRITICAL_SECTION来尝试解决这个问题.本文首先介绍下如何使用关键段,然后再深层次的分析下关键段的实现机制与原理.关键段CRITIC ...

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

    本文首先介绍下如何使用关键段,然后再深层次的分析下关键段的实现机制与原理. 关键段CRITICAL_SECTION一共就四个函数,使用很是方便.下面是这四个函数的原型和使用说明. 函数功能:初始化 函 ...

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

    上一篇<秒杀多线程第四篇 一个经典的多线程同步问题>提出了一个经典的多线程同步互斥问题,本篇将用关键段CRITICAL_SECTION来尝试解决这个问题. 本文首先介绍下如何使用关键段,然 ...

  9. windows多线程编程星球(一)

    以前在学校的时候,多线程这一部分是属于那种充满好奇但是又感觉很难掌握的部分.原因嘛我觉得是这玩意儿和编程语言无关,主要和操作系统的有关,所以这部分内容主要出现在讲原理的操作系统书的某一章,看完原理是懂 ...

随机推荐

  1. idea ssm项目出现日志中文乱码,封装的json中的msg字段中文乱码(但是json封装的bean中的字段不乱码)等其他各种项目下的中文乱码解决方案

    开头划重点!(敲黑板):rebuild和mvn package的循环往复好几次的操作是解决这个问题的最主要的方法! 经过多次试验,发现这样做就可以正常显示中文了 我说为什么有时候乱码,有时候中文正常, ...

  2. 新建React Native项目步骤

    根据官方环境 https://reactnative.cn/docs/getting-started/ 搭建好之后 1.新建项目 打开React Native 命令行工具,并输入 react-nati ...

  3. js获取字符串字节数方法小结

    js获取字符串字节数的方法.分享给大家供大家参考.具体如下: 大家都知道,获取字符串的长度可用length来获取,那么获取这段字符串的字节数呢? 英文字母肯定lenght和字节数都一样:都是1而中文l ...

  4. 【PaPaPa】实现缓存决策 - 让你的缓存变的有智慧

    我有话说 本来这一篇我打算放到后面再说,可是之前泄漏了一点关于缓存决策的代码后被好多人催更了. 在此感谢大家的支持,让我更有动力的写这个系列.你们的关注让我觉得我的决定是对的,我会坚持下去把这个项目做 ...

  5. 大神教你零基础学PS,30堂课从入门到精通

    ps视频教程,ps自学视频教程.ps免费视频教程下载,大神教你零基础学PS教程视频内容较大,分为俩部分: 大神教你零基础学PS--30堂课从入门到精通第一部分:百度网盘,https://pan.bai ...

  6. [PLC]ST语言三:OUT/OUT_T/OUT_C/OUT_C-C32

    一:OUT/OUT_T/OUT_C/OUT_C-C32               说明:简单的顺控指令不做其他说明. 控制要求:无 编程梯形图: 结构化编程ST语言: (*OUT(EN,D);*) ...

  7. Youtube高清视频下载的3种方法

    经常看视频的朋友都听说或使用过youtube,  它是一个综合性的视频网站,包含的内容多种多样,能满足不同的人的需求,最要的是广告少,资源良心,不像有些网站,动不动就是1分种以上的长广告.有些因为工作 ...

  8. CSS清浮动办法

    骨灰级解决办法: .clear{clear:both;height:0;overflow:hidden;} 上诉办法是在需要清除浮动的地方加个div.clear或者br.clear,我们知道这样能解决 ...

  9. GTK 预置对话框 GtkDialog 文件/颜色/字体选取等 GtkFileSelection

    (GTK2) 文档链接 作用:打开一个预置的对话框,如文件选取对话框 GtkFileSelection 效果下图所示 ╰── GtkDialog ├── GtkAboutDialog ├── GtkC ...

  10. 简单理解DNS解析流程(一)

    0x0 简单理解dns DNS服务器里存着一张表 表中放着域名和IP地址,域名和IP地址以映射关系保存,即一对一 浏览器访问某个域名,实际上是访问它的ip地址 所以浏览器需要知道域名对应的ip地址 如 ...