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

前面提出了一个经典的多线程同步互斥问题,本篇将用关键段CRITICAL_SECTION来尝试解决这个问题。

本文先介绍如何使用关键段,然后再深层次的分析下关键段的实现机制和原理。

关键段CRITICAL_SECTION一共就四个函数,下面说一下这四个函数的原型和使用。

函数功能:初始化
函数原型:
void InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
函数说明:定义关键段变量后必须先初始化。函数功能:销毁
函数原型:
void DeleteCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
函数说明:用完之后记得销毁。函数功能:进入关键区域
函数原型:
void EnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
函数说明:系统保证各线程互斥的进入关键区域。函数功能:离开关关键区域
函数原型:
void LeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

然后在前面的这个经典多线程问题中设置两个关键区域。一个是主线程在递增子线程序号时,另一个是各个线程互斥的访问输出全局资源。

#include <stdio.h>
#include <process.h>
#include <windows.h>long g_nCount;
unsigned int __stdcall Fun(void *pPM);
const int THREAD_NUM = 10;
//关键段变量声明
CRITICAL_SECTION g_csThreadParameter, g_csThreadCode;int main()
{//关键段初始化InitializeCriticalSection(&g_csThreadParameter);InitializeCriticalSection(&g_csThreadCode);HANDLE handle[THREAD_NUM];g_nCount  = 0;int i = 0;while(i < THREAD_NUM){EnterCriticalSection(&g_csThreadParameter);//进入子线程序号关键区域handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);i++;}WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);DeleteCriticalSection(&g_csThreadCode);DeleteCriticalSection(&g_csThreadParameter);return 0;
}unsigned int __stdcall Fun(void *pPM)
{int nThreadNum = *(int *)pPM;LeaveCriticalSection(&g_csThreadParameter);//离开子线程序号关键区域Sleep(50);EnterCriticalSection(&g_csThreadCode);//进入各子线程的互斥区g_nCount++;Sleep(0);printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_nCount);LeaveCriticalSection(&g_csThreadCode);//离开各个子线程的互斥区域return 0;
}

运行结果:

image

可以看到各个子线程能够成功的访问和输出全局资源了,但是主线程和子线程之间的同步还是有点问题。


想找到问题,最直接的方法就是在程序中添加断点来查看程序的执行流程:

image

正常来说,两个断点应该轮流执行才可以,但是实际的调试过程中却发现实际的情况不是如此。在没有运行第二个断点的情况下,主线程可以多次通过第一个断点,也就是:

 EnterCriticalSection(&g_csThreadParameter); 

这个语句。这说明主线程可以多次进入这个关键区,而不用子线程去帮助它释放关键区的变量。


找到了主线程不能和子线程同步的原因了,那么为什么他们功能用关键段进行同步呢?

先找到关键段CRITICAL_SECTION的定义吧,它在WinBase.h中被定义成RTL_CRITICAL_SECTION。而RTL_CRITICAL_SECTION在WinNT.h中声明,它其实是个结构体:

typedef struct _RTL_CRITICAL_SECTION {PRTL_CRITICAL_SECTION_DEBUG DebugInfo;LONG LockCount;LONG RecursionCount;HANDLE OwningThread; // from the thread's ClientId->UniqueThreadHANDLE LockSemaphore;DWORD SpinCount;
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
各个参数的解释如下:
第一个参数:PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
调试用的。
第二个参数:LONG LockCount;
初始化为-1,n表示有n个线程在等待。
第三个参数:LONG RecursionCount;  
表示该关键段的拥有线程对此资源获得关键段次数,初为0。
第四个参数:HANDLE OwningThread;  
即拥有该关键段的线程句柄,微软对其注释为——from the thread's ClientId->UniqueThread
第五个参数:HANDLE LockSemaphore;
实际上是一个自复位事件。
第六个参数:DWORDSpinCount;    
旋转锁的设置,单CPU下忽略

由这个结构可以知道关键段会记录拥有该关键段的线程句柄即关键段是有“线程所有权”概念的。事实上它会用第四个参数OwningThread来记录获准进入关键区域的线程句柄,如果这个线程再次进入,EnterCriticalSection()会更新第三个参数RecursionCount以记录该线程进入的次数并立即返回让该线程进入。其它线程调用EnterCriticalSection()则会被切换到等待状态,一旦拥有线程所有权的线程调用LeaveCriticalSection()使其进入的次数为0时(注意这句话的意思是Enter和Leave必须在同一个线程中),系统会自动更新关键段并将等待中的线程换回可调度状态。

因此可以将关键段比作旅馆的房卡,调用EnterCriticalSection()即申请房卡,得到房卡后自己当然是可以多次进出房间的,在你调用LeaveCriticalSection()交出房卡之前,别人自然是无法进入该房间。

回到这个经典线程同步问题上,主线程正是由于拥有“线程所有权”即房卡,所以它可以重复进入关键代码区域从而导致子线程在接收参数之前主线程就已经修改了这个参数。所以关键段可以用于线程间的互斥,但不可以用于同步。


另外,由于将线程切换到等待状态的开销较大,因此为了提高关键段的性能,Microsoft将旋转锁合并到关键段中,这样EnterCriticalSection()会先用一个旋转锁不断循环,尝试一段时间才会将线程切换到等待状态。下面是配合了旋转锁的关键段初始化函数。

函数功能:初始化关键段并设置旋转次数
函数原型:
BOOLInitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTIONlpCriticalSection,DWORDdwSpinCount);
函数说明:旋转次数一般设置为4000。函数功能:修改关键段的旋转次数
函数原型:
DWORDSetCriticalSectionSpinCount(LPCRITICAL_SECTIONlpCriticalSection,DWORDdwSpinCount);

最后总结下关键段:

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

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

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

转载于:https://www.cnblogs.com/stemon/p/4390189.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/289295.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何设置Matlab输出到Word中图片的大小

方法一:快速型核心方法:设置Word显示比例100%,Matlab的图像大小与屏幕所见的相同;根据Word的排版要求,估计Matlab图像在Word中的大小,进而调整图像大小。具体操作:(以下操作应对图像绘制完成后进行,图像绘制中字体大小、线宽等都与排版需要一致,比如8号字体,需要故意…

服务器线程数一直增加,.NET Core中遇到奇怪的线程死锁问题:内存与线程数不停地增长...

一个 asp.net core 站点&#xff0c;之前运行在Linux 服务器上&#xff0c;运行一段时间后有时站点会挂掉&#xff0c;在日志中记录很多“EMFILE too many open files”的错误&#xff1a;Microsoft.AspNetCore.Server.Kestrel.Internal.Networking.UvException: Error -24 EMF…

python种颜色循环_Python 实现一个颜色色值转换的小工具

需求说明 公司的 UI 设计小哥&#xff0c;已经转用 Zeplin 很久了。Zeplin 的设计稿展示页面的颜色色值使用十进制的 RGB 表示的&#xff0c;在 Android 中的颜色表示大多情况下都需要十六进制的 RGB 表示。我的数学没有好到直接看到十进制就可以心算得到十六进制的结果&#x…

bugly中批量隐藏版本

App项目使用Bugly的内测分发功能进行整包的测试&#xff0c;但日积月累之后&#xff0c;版本就会特别多。而线上同时跑的版本可能不过三个左右&#xff0c;那么多版本会干扰到查看崩溃、选择版本&#xff0c;如何隐藏呢&#xff1f; 右上角&#xff0c;更多 –> 产品设置 –…

检查点(Checkpoint)过程如何处理未提交的事务

每次我讲解SQL Server之前&#xff0c;我都会先简单谈下当我们执行查询时&#xff0c;在SQL Server内部发生了什么。执行一个SELECT语句非常简单&#xff0c;但是执行DML语句更加复杂&#xff0c;因为SQL Server要修改内存中的相关页&#xff0c;并在事务日志里记录整个事务。 …

axure如何导出原件_axure导出_axure怎么导出流程图

摘要 腾兴网为您分享:axure怎么导出流程图&#xff0c;随身行&#xff0c;轻轻家教&#xff0c;钱宝宝&#xff0c;美甲帮等软件知识&#xff0c;以及winrar美化版&#xff0c;九秀直播软件&#xff0c;中医养生平台&#xff0c;酚酞瓜&#xff0c;4399dm&#xff0c;移动硬盘检…

机器学习工作坊 - 计算机视觉

点击上方蓝字关注我们&#xff08;本文阅读时间&#xff1a;2分钟&#xff09;活动介绍四月的 MS Learn 学堂&#xff0c;我们将进入机器学习专题。 本月三期 MS Learn 学堂&#xff0c;我们会以工作坊的形式&#xff0c;结合具体应用实例&#xff0c;带领大家实践计算机视觉、…

React Native之提示Unable to load script from assets ‘index.android.bundle

1 问题 Unable to load script from assets index.android.bundle. Make sure your bundle is packaged correctly or youre running a packager server. 2 分析 在android的assets目录下面,没有找到index.android.bundle文件 2 解决办法 1) 在app/src/main目录下新建assets…

SQL分组处理气象数据及注意事项

问题&#xff1a;从全国所有气象站点数据气候数据表中&#xff08;温度&#xff0c;降水量&#xff0c;风速等&#xff09;找出研究区气象站点及所需气象信息。 解决&#xff1a; select 台站号 as 台站号,年 as 年,降水量 as 降水量,平均气温 as 年均温 from 站点气候年数据 …

服务器硬盘如何验收,系列服务器及存储测试安装验收报告.doc

系列服务器及存储测试安装验收报告p系列UNIX服务器测试安装说明1&#xff0e;测试p系列UNIX服务器系统开关机及系统启动测试目的&#xff1a;系统操作系统是否安装完好&#xff0c;主机能否正常开关机。测试方法&#xff1a;用正常关机手段关闭主机(# shutdown)&#xff0c;然后…

arcgis server 无法手动删除切片

背景 问题 场景如下&#xff1a; 切片放置在专门的文件服务器上&#xff0c;通过unc共享路径对外共享。文件服务器的OS为windows server2008R2想手动更新切片服务的切片。发现同一切片服务&#xff0c;有的比例级别文件夹可以删除或者重命名。有的比例级别不可以。不能删除的比…

风变编程python离线版_如何看待风变编程的Python网课

第一次接触Python&#xff0c;其实就是朋友一句话&#xff0c;听说派森挺厉害的&#xff0c;时间大概是两个月前&#xff0c;当时网上搜了下&#xff0c;噢&#xff0c;英文叫Python&#xff0c;一种编程语言&#xff0c;然后就没有然后了... 最近朋友圈偶尔发现有个Python小课…

千寻和省cors精度对比_为什么使用千寻cors服务?它有什么优势?

过去20年&#xff0c;黑白屏手机升级成为智能手机、多数绿皮火车升级成了高铁、数控机床替代了大部分切割工人……科技创新正推动各行业向着便捷、高效、低成本的方向变革。技术升级为商业变革提供巨大动力&#xff0c;商业应用验证技术发展方向。高精度定位将成为公共服务在四…

python exception的传递

try: block except1: except2: ... 如果block中出现了except&#xff0c;那么会先取匹配except1&#xff0c;如果匹配了&#xff0c;进行处理&#xff0c;程序继续执行。 如果except1没有匹配上&#xff0c;那么继续匹配except2。 如果该层的try except都没有匹配到&#xff0c…

Hadoop 2.0 中的资源管理框架 - YARN(Yet Another Resource Negotiator)

1. Hadoop 2.0 中的资源管理 http://dongxicheng.org/mapreduce-nextgen/hadoop-1-and-2-resource-manage/Hadoop 2.0指的是版本为Apache Hadoop 0.23.x、2.x或者CDH4系列的Hadoop&#xff0c;内核主要由HDFS、MapReduce和YARN三个系统组成&#xff0c;其中&#xff0c;YARN是一…

React Native实现js调用安卓原生代码

1 问题 实现js调用安卓原始代码,直接上代码,简单粗暴 2 代码实现 1) 实现一个继承ReactContextBaseJavaModule的类,MyToastModule.java文件如下 public class MyToastModule extends ReactContextBaseJavaModule {public MyToastModule(ReactApplicationContext reactContext)…

Matlab三种归一化方法

归一化的具体作用是归纳统一样本的统计分布性。归一化在0-1之间是统计的概率分布,归一化在-1--+1之间是统计的坐标分布。归一化有同一、统一和合一的意思。无论是为了建模还是为了计算,首先基本度量单位要同一,神经网络是以样本在事件中的统计分别几率来进行训练(概率计算)…

发布nuget包的正确姿势---cicd自动打包发布

最轻便的发布nuget包方式&#xff0c;方便cicd自动打包发布nuget包首先新建项目项目名随便取&#xff0c;这里就叫它GuiH.ClassLibrary默认即可&#xff0c;需要改目标版本时&#xff0c;等创建好再改项目创建好了随便写个接口方法namespace GuiH.ClassLibrary {public class C…

我的世界服务器物品属性,属性 - Minecraft Wiki,最详细的官方我的世界百科

属性(Attributes)是生物和玩家身上的增益/减益特性系统。属性也存在修饰符(Modifiers)中&#xff0c;用于调整属性的强度。属性应用[]当应用到一个物品&#xff0c;一个物品的修饰符将增加或减少以修正相应的属性下面的命令将给最近的玩家一把增加20( 10)点额外伤害的钻石剑&a…