当前位置: 首页 > java >正文

【C语言】Windows下的多线程编程-关键段(临界区)

临界区(关键段):线程同步方式,用于限制“公用代码”一次只能被一个线程使用

关键段定义

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;			//初始化为-1,n表示有n个线程在等待LONG RecursionCount;	//表示该关键段的拥有线程对此资源获得关键段次数,初初始化为0HANDLE OwningThread;    //即拥有该关键段的线程句柄HANDLE LockSemaphore;	//实际上是一个自复位事件ULONG_PTR SpinCount;    //旋转锁的设置,单CPU下忽略
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;

InitializeCriticalSection初始化

定义关键段变量后必须先初始化

/** \brief 	初始化一个临界资源对象** \param   lpCriticalSection 指向临界区对象的指针* \return  无**/
void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

DeleteCriticalSection销毁

用完之后记得销毁临界资源对象


/** \brief 	销毁一个临界资源对象** \param   lpCriticalSection 指向临界区对象的指针*				先前必须已将该对象初始化 InitializeCriticalSection初始化函数* \return  无**/
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

EnterCriticalSection进入关键区域

/** \brief 	进入关键区域** \param   lpCriticalSection 指向临界区对象的指针* \return  无**/
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

LeaveCriticalSection离开关键区域

/** \brief 	离开关键区域** \param   lpCriticalSection 指向临界区对象的指针* \return  无**/
void LeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

示例1

所有子线程都访问共享资源,为了避免冲突,需要加上关键段,保证每次共享资源只能被一个线程访问。在子线程中每访问共享资源则记录一次。

#include <stdio.h>
#include <Windows.h>
#include <process.h>#define THREAD_COUNT 30U	//子线程数目void Thread(void*);volatile int count = 0;//共享资源CRITICAL_SECTION cs1;//关键段变量int main()
{HANDLE th[THREAD_COUNT] = { 0 };int num = 30;/* 初始化关键段 */InitializeCriticalSection(&cs1);//模拟次数while (num--){count = 0;//重置for (size_t i = 0; i < THREAD_COUNT; i++){/* 创建线程 */th[i] = _beginthread(Thread, 0, NULL);}//等待所有子线程结束WaitForMultipleObjects(THREAD_COUNT, th, TRUE, INFINITE);printf("共 %d 个子线程  共享资源次数为%d\n", THREAD_COUNT, count);}/* 销毁关键段 */DeleteCriticalSection(&cs1);return 0;
}void Thread(void* arg)
{Sleep(100);/* 进入关键段 */EnterCriticalSection(&cs1);count++;//子线程中 每访问一次共享资源则记录一次/* 离开关键段 */LeaveCriticalSection(&cs1);
}

输出结果
在这里插入图片描述

示例2

与示例1要求一样,只不过只有三个子线程。线程A将访问次数加1,线程B将访问次数加50,线程C将访问次数加100。

#include <stdio.h>
#include <Windows.h>
#include <process.h>#define THREAD_COUNT 3U	//子线程数目void Thread1(void*);
void Thread2(void*);
void Thread3(void*);volatile int count = 0;//共享资源CRITICAL_SECTION cs1;//关键段变量int main()
{HANDLE th[THREAD_COUNT] = { 0 };int num = 30;/* 初始化关键段 */InitializeCriticalSection(&cs1);//模拟次数while (num--){count = 0;//重置/* 创建子线程 */th[0] = _beginthread(Thread1, 0, NULL);th[1] = _beginthread(Thread2, 0, NULL);th[2] = _beginthread(Thread3, 0, NULL);//等待所有子线程结束WaitForMultipleObjects(THREAD_COUNT, th, TRUE, INFINITE);printf("共 %d 个子线程  共享资源次数为%d\n", THREAD_COUNT, count);}/* 销毁关键段 */DeleteCriticalSection(&cs1);return 0;
}void Thread1(void* arg)
{Sleep(100);/* 进入关键段 */EnterCriticalSection(&cs1);count++;//子线程中 每访问一次共享资源则记录一次/* 离开关键段 */LeaveCriticalSection(&cs1);
}void Thread2(void* arg)
{Sleep(100);/* 进入关键段 */EnterCriticalSection(&cs1);count += 50;//访问次数翻倍/* 离开关键段 */LeaveCriticalSection(&cs1);
}void Thread3(void* arg)
{Sleep(100);/* 进入关键段 */EnterCriticalSection(&cs1);count += 100;//访问次数加100/* 离开关键段 */LeaveCriticalSection(&cs1);
}

输出结果
在这里插入图片描述
说明,每个需要访问共享资源的线程,都需要加上关键段来限制共享资源每次只被一个线程访问,如果有一个线程没有加上关键段的限制,那么关键段将没有意义。

void Thread3(void* arg)
{Sleep(100);count += 100;//访问次数加100
}

那么可能会出现
在这里插入图片描述

旋转锁

当线程试图进入另一个线程拥有的关键代码段时,调用线程就立即被置于等待状态。这意
味着该线程必须从用户方式转入内核方式(大约1 0 0 0个C P U周期)。这种转换是要付出很大代价的。

因此, InitializeCriticalSectionAndSpinCount 的作用不同于InitializeCriticalSection 之处就在于设置了一个循环锁,不至于使线程立刻被置于等待状态而耗费大量的CPU周期,而在dwSpinCount后才转为内核方式进入等待状态。通常dwSpinCount设为4000较为合适。

下面是配合了旋转锁的关键段初始化函数

初始化关键段并设置旋转次数

/** \brief 	初始化关键段并设置旋转次数** \param   lpCriticalSection 临界资源对象指针* \param   dwSpinCount 临界区对象的旋转计数。在单处理器系统上,自旋计数被忽略,临界区自旋计数被设置为0* \return  成功并返回一个非零值**/
BOOL InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount);

修改关键段的旋转次数

/** \brief 	修改关键段的旋转次数** \param   lpCriticalSection 临界资源对象指针* \param   dwSpinCount 临界区对象的旋转计数。在单处理器系统上,自旋计数被忽略,临界区自旋计数被设置为0* \return  返回临界区的前一次旋转计数**/
DWORD SetCriticalSectionSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount);
http://www.xdnf.cn/news/11094.html

相关文章:

  • LARGE_INTEGER 转换 __int64
  • 进化计算(八)——MOEA/D算法详解Ⅱ
  • 12036 抢票助手开源,星标 12.7K !
  • B2B2C电商app系统开发(java开源,快速搭建)
  • 【一文读懂】什么是磁带存储?为何焕发第二春?
  • 【Laravel系列4.3】模型Eloquent ORM的使用(一)
  • 5000字详解性能需求
  • 滚动条样式修改
  • 内网端口映射工具有哪些?
  • 蝶阀的作用介绍
  • 史上最全网络安全站点集合
  • Java面试宝典
  • 分享126个图片JS特效,总有一款适合您
  • Nios II嵌入式软处理器提升系统性能方式详解
  • Happytime RTSP可用于流式传输各种设备和文
  • 俄罗斯 搜索引擎 邮箱创建
  • 使用 iMacros 来自动化日常的工作
  • LAMP架构
  • erp是什么意思?ERP系统是什么
  • 使用Plist编辑器——简单入门指南
  • 前HP大中华区总裁孙振耀退休感言
  • Java小白(学习Java详细讲解)从入门到精通
  • 一文带你了解红墨水实验!
  • 华为交换机配置入门指南:从零开始,轻松掌握基础配置
  • 一文读懂背照式CMOS图像传感器
  • 报表学习总结(一)——ASP.NET 水晶报表(Crystal Reports)的简单使用
  • 第十四章、DNS域名解析服务
  • 15款高效网页制作工具,助你快速建站!
  • 名词解释-【XFire】
  • 【Java 常用类】java.lang包(6)String类的常用方法