一、问题回顾

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

这是上一篇中的程序:


#include <stdio.h>
#include <windows.h> const unsigned int THREAD_NUM = 50;
unsigned int g_Count = 0;
DWORD WINAPI ThreadFunc(LPVOID); int main()
{
HANDLE hThread[THREAD_NUM];
for (int i = 0; i < THREAD_NUM; i++)
{
hThread[i] = CreateThread(NULL, 0, ThreadFunc, 0, 0, NULL); // 创建线程
}
WaitForMultipleObjects(THREAD_NUM, hThread, true, INFINITE); //一直等待,直到所有子线程全部返回
printf(" 总共 %d 个线程给 g_Count 的值加一,现在 g_Count = %d\n", THREAD_NUM, g_Count);
return 0;
} DWORD WINAPI ThreadFunc(LPVOID p)
{
Sleep(50);
g_Count++;
Sleep(50); return 0;
}

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

(一)CriticalSection 声明

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

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

typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;

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



typedef struct _RTL_CRITICAL_SECTION {
PRTL_CRITICAL_SECTION_DEBUG DebugInfo; //
// The following three fields control entering and exiting the critical
// section for the resource
// LONG LockCount;
LONG RecursionCount;
HANDLE OwningThread; // from the thread's ClientId->UniqueThread
HANDLE LockSemaphore;
ULONG_PTR SpinCount; // force size on 64-bit systems when packed
} 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.函数功能:初始化,定义关键段变量后必须先初始化。

void InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

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

void DeleteCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

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

void EnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

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

void LeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

三、实例

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


#include <stdio.h>
#include <windows.h> const unsigned int THREAD_NUM = 50;
unsigned int g_Count = 0;
CRITICAL_SECTION cs; //声明关键段
DWORD WINAPI ThreadFunc(LPVOID); int main()
{
InitializeCriticalSection(&cs); // 初始化关键段
HANDLE hThread[THREAD_NUM];
for (int i = 0; i < THREAD_NUM; i++)
{
hThread[i] = CreateThread(NULL, 0, ThreadFunc, 0, 0, NULL); // 创建线程
}
WaitForMultipleObjects(THREAD_NUM, hThread, true, INFINITE); //一直等待,直到所有子线程全部返回
printf(" 总共 %d 个线程给 g_Count 的值加一,现在 g_Count = %d\n", THREAD_NUM, g_Count);
DeleteCriticalSection(&cs); //销毁关键段
return 0;
} DWORD WINAPI ThreadFunc(LPVOID p)
{
Sleep(50);
EnterCriticalSection(&cs); // 进入关键段
g_Count++;
LeaveCriticalSection(&cs); // 离开关键段
Sleep(50); return 0;
}

运行结果如下图所示,给全局资源 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. Jquery 实现select 3级级联查询

    实现级联效果的思路: 1. 页面加载时,先显示第一级select,第二.三级的select隐藏,根据第一级select值的改变,再显示第二级select,依次类推: 2.只从后台获取第一级select ...

  2. 【HAOI2016】放棋子

    题面 题解 任意两个障碍不在同一列 要求你放$N$个棋子也满足每行只有一枚棋子,每列只有一枚棋子的限制. 这™不就是个错排吗??? $$ h_i=(n-1)(h_{i-1}+h_{i-2}),h_1= ...

  3. Oracle GUID转换为String

    Oracle中guid属于Raw(16)类型, 查询的时候如果不使用下面的函数, 程序中得到的是数组(byte[]). 在extjs环境下, 会带来数组的反序列化问题(newtonsoft.json) ...

  4. [翻译] Python 3.5中async/await的工作机制

    Python 3.5中async/await的工作机制 多处翻译出于自己理解,如有疑惑请参考原文 原文链接 身为Python核心开发组的成员,我对于这门语言的各种细节充满好奇.尽管我很清楚自己不可能对 ...

  5. Rmdir方法

    删除现有目录或文件夹. 语法 RmDir路径 所需的_路径_参数标识的目录或文件夹要移除的字符串表达式. _路径_可以包含驱动器. 如果没有指定驱动器, RmDir 将删除当前驱动器上的目录或文件夹. ...

  6. 判断浏览器是chrome,Opera,Safari,Mac

    function(){ return { isSafari: (navigator.userAgent.indexOf('Safari')>=0 || navigator.userAgent.i ...

  7. L2-016 愿天下有情人都是失散多年的兄妹

    L2-016 愿天下有情人都是失散多年的兄妹 (25 分)   呵呵.大家都知道五服以内不得通婚,即两个人最近的共同祖先如果在五代以内(即本人.父母.祖父母.曾祖父母.高祖父母)则不可通婚.本题就请你 ...

  8. 如何在忘记mysql的登录密码时更改mysql登录的密码(window及linux)

    最近一直在边学习边开发java项目,理所当然的就少不了跟数据库打交道了,但是有时候就会脑子一短路,把mysql的登录密码给忘记了,这个时候我们又很急切的需要进到数据库中查看数据,那这个时候要怎么才能改 ...

  9. Siki_Unity_4-4_丛林战争_Socket/TCP网络游戏开发

    Unity 4-4 丛林战争(Socket/TCP网络游戏开发) 任务1:素材.演示.Prerequisite 使用c#的有关TCP的底层API进行服务器端的开发(直接通过socket进行通信) 功能 ...

  10. log4cpp简单使用及踩到的坑

    log4cpp是log4j的一个扩展, C++开发者可用该库记录日志,可输出到终端,亦可保存到文件. 下面简单demo展示如何输出日志到输出终端. #include <iostream> ...