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

[C++ 网络协议] Windows中的线程同步

目录

1. 用户模式(User mode)和内核模式(Kernal mode)

2. 用户模式的同步(CRITICAL_SECTION)

3. 内核模式同步

3.1 互斥量

3.2 信号量

3.3 事件对象

4. 实现Windows平台的多线程服务器端


1. 用户模式(User mode)和内核模式(Kernal mode)

Windows操作系统的运行方式是“双模式操作”:

  • 用户模式:运行应用程序的基本模式,禁止访问物理设备,而且会限制访问的内存区域。
  • 内核模式:操作系统运行时的模式,不仅不会限制访问的内存区域,而且访问的硬件设备也不会受损。

在应用程序的运行过程中,Windows操作系统会在用户模式和内核模式之间切换。比如说,在Windows中创建线程,虽然创造线程的请求是由应用程序的函数调用来完成的,但是线程是属于操作系统的,所以Windows会先从用户模式切换到内核模式,接着创建线程,分配资源,创建内核对象。

为什么要定义这两种模式?

答:为了提高安全性。因为应用程序在运行时如果发生错误,就可能会破坏操作系统的各种资源。尤其是C/C++可以进行指针运算,就很容易发生这种问题。而用户模式可以保护与操作系统有关的内存区域。所以应用程序在运行时发生错误也只会终止应用程序的运行,而不会终止操作系统。

那这两种模式只有优点吗?

答:不是,频繁的模式切换对系统而言也是一种负担,会影响性能

2. 用户模式的同步(CRITICAL_SECTION)

用户模式的同步是在用户模式下的同步,这意味着,无需操作系统的帮助而在应用程序级别进行的同步 ,即无需进行内核模式的切换。

  • 优点:速度快。

  • 缺点:功能上存在局限性。

创建CRITICAL_SECTION对象:

#include<windows.h>void InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection    //传入需要初始化的CRITICAL_SECTION对象//的地址值
);

CRITICAL_SECTION对象,不是内核对象。其只是一把进入临界区的“钥匙”。要进入临界区就提供这一把钥匙,离开临界区就要上交钥匙。

“销毁”CRITICAL_SECTION对象:

#include<windows.h>void DeleteCriticalSection(
LPCRITICAL_SECTION lpCriticalSection    //传入需要销毁的CRITICAL_SECTION对象//的地址值
);

实际上这个函数并不是销毁CRITICAL_SECTION对象,而是销毁CRITICAL_SECTION对象使用过的(或与其相关的)资源。

上锁:

#include<windows.h>void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

解锁:

#include<windows.h>void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);

3. 内核模式同步

内核模式的同步就是在内核模式下的同步。意味着都是通过操作系统的帮助下进行的线程同步。可以实现跨进程之间进行线程同步。(因为内核对象属于操作系统,而不属于进程)

  • 优点:比用户模式提供的功能更多、可以指定超时,防止产生死锁
  • 缺点:速度相对用户模式来说较慢

3.1 互斥量

互斥量对象是"auto-reset"模式的内核对象。([C++ 网络协议] Windows平台下的线程里有解释

创建互斥量:

#include<windows.h>HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,    //传递安全相关的配置信息,传NULL使用默认安全配置
BOOL bInitialOwner,                         //TRUE,创建的互斥量属于调用该函数的线程,//同时进入non-signaled状态//FALSE,创建处的互斥量对象不属于任何线程,//同时进入signaled状态
LPCTSTR lpName                              //用于命名互斥量对象,传NULL表示无名
);
成功返回创建的互斥量对象句柄
失败返回NULL

bInitialOwner参数:其实就是初始化互斥量的状态的值,为TRUE初始化为non-signaled状态。为FALSE初始化为signaled状态。

销毁互斥量:

#include<windows.h>BOOL CloseHandle(HANDLE hObject);
成功返回TRUE
失败返回FALSE

获取互斥量(上锁):

#include<windows.h>DWORD WaitForSingleObject(
HANDLE hHandle,            //查看状态的内核对象句柄
DWORD dwMilliseconds       //以1/1000秒为单位指定超时时间,传递INFINITE会阻塞住,//直到内核对象变为signaled状态
);
成功返回事件信息,事件信息:成功进入signaled状态返回WAIT_OBJECT_0,超时返回WAIT_TIMEOUT
失败返回WAIT_FAILED

这个函数就是“单个内核对象状态的查看”。所以可以判断出,线程是否被阻塞,看的是互斥量内核对象的状态

释放互斥量(解锁):

#include<windows.h>BOOL ReleaseMutex(HANDLE hMutex);
成功返回TRUE,并使互斥量重新进入signaled状态
失败返回FALSE

例:

HANDLE mutex;
int main()
{mutex=CreateMutex(NULL,FALSE,NULL);    //mutex初始化为signaled状态......
}unsigned WINAPI threadOne(void* arg)
{WaitForSingleObject(mutex,INFINITE);   //mutex为signaled状态,则继续执行//临界区开始                            //因为mutex是auto-reset模式的内核对象,所以mutex会变为non-signaled状态......                                 //临界区结束ReleaseMutex(mutex);                   //释放mutex将其设置为signaled状态  
}unsigned WINAPI threadTwo(void* arg)
{WaitForSingleObject(mutex,INFINITE);  //因为上面线程先执行,mutex变为了non-signaled状态,所以线程阻塞住,等待mutex的释放//临界区开始......//临界区结束ReleaseMutex(mutex);
}

3.2 信号量

创建信号量:

#include<windows.h>HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,    //安全配置信息,默认安全设置传递NULL
LONG lInitialCount,                             //指定信号量的初始值,应>0,且<lMaximumCount
LONG lMaximumCount,                             //信号量的最大值,为1,则是二进制信号量(只能表示0或1)
LPCTSTR lpName                                  //命名信号量对象,传NULL为无名
);
成功返回创建的信号量对象的句柄
失败返回NULL

销毁信号量:(和互斥量是一样的销毁句柄语句)

#include<windows.h>BOOL CloseHandle(HANDLE hObject);
成功返回TRUE
失败返回FALSE

通过信号量,判断线程是否阻塞:

#include<windows.h>DWORD WaitForSingleObject(
HANDLE hHandle,            //查看状态的内核对象句柄
DWORD dwMilliseconds       //以1/1000秒为单位指定超时时间,传递INFINITE会阻塞住,//直到内核对象变为signaled状态
);
成功返回事件信息,事件信息:成功进入signaled状态返回WAIT_OBJECT_0,超时返回WAIT_TIMEOUT
失败返回WAIT_FAILED

这个函数也是“单个内核对象状态的查看”。返回的同时信号量-1。

利用信号量为0时,进入non-signaled状态,大于0时,进入signaled状态,来进行同步。

释放信号量:

#include<windows.h>BOOL ReleaseSemaphore(
HANDLE hSemaphore,        //传递需要释放的信号量
LONG lReleaseCount,       //释放信号量会增加,这个参数指定要增加的值,超过最大值则不增加且返回FALSE
LPLONG lpPreviousCount    //保存修改之前值得变量地址,不需要的话可传NULL
);
成功返回TRUE
失败返回FALSE

例:

HANDLE semOne;
HANDLE semTwo;int main()
{semOne=CreateSemaphore(NULL,0,1,NULL);    //semOne为non-signaled状态semTwo=CreateSemaphore(NULL,1,1,NULL);    //semTwo为signaled状态......
}unsigned WINAPI threadOne(void* arg)
{WaitForSingleObject(semTwo,INFINITE);    //semTwo为signaled状态,继续执行,且semTwo-1变为non-signaled状态//临界区......//临界区ReleaseSemaphore(semOne,1,NULL);        //semOne+1,变为signaled状态
}unsigned WINAPI threadTwo(void* arg)
{WaitForSingleObject(semOne,INFINITE);   //通过上一个线程,semOne为signaled状态,开始执行,且semOne-1,变为non-signaled状态//临界区......//临界区ReleaseSemaphore(semTwo,1,NULL);
}

3.3 事件对象

基于事件对象的线程同步,与前两种同步方式有很大不同,因为事件对象创建时,可以选择是“auto-reset”模式还是“manual-reset”模式。这也是其进行同步的方式。

创建事件对象:

#include<windows.h>HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,    //安全配置信息,默认安全设置传递NULL
BOOL bManualReset,                          //TRUE,创建manual-reset模式的事件对象//FALSE,创建auto-reset模式的事件对象
BOOL bInitialState,                         //TRUE,事件对象初始化为signaled状态//FALSE,事件对象初始化为non-signaled状态
LPCTSTR lpName                              //命名信号量对象,传NULL为无名
);
成功返回创建的事件对象句柄
失败返回NULL

销毁事件对象(销毁句柄语句):

#include<windows.h>BOOL CloseHandle(HANDLE hObject);
成功返回TRUE
失败返回FALSE

更改事件对象状态:

#include<windows.h>BOOL ResetEvent(HANDLE hEvent);    //将事件对象设置为non-signaled状态
BOOL SetEvent(HANDLE hEvent);      //将事件对象设置为signaled状态
成功返回TRUE
失败返回FALSE

例如:实现两个线程同时退出阻塞状态的情景。

HANDLE event;int main()
{event=CreateEvent(NULL,TRUE,FALSE,NULL);    //将事件对象设置为mamual-reset模式,并初始化为non-signaled状态......SetEvent(event);    //将事件对象设置为singnaled状态......
}unsigned WINAPI threadOne(void* arg)
{WaitForSingleObject(event,INFINITE);    //阻塞,等待事件对象设置为singaled状态//临界区......//临界区
}unsigned WINAPI threadTwo(void* arg)
{WaitForSingleObject(event,INFINITE);    //阻塞,等待事件对象设置为singaled状态//临界区......//临界区
}

4. 实现Windows平台的多线程服务器端

相关文章:

[C++ 网络协议] Windows中的线程同步

目录 1. 用户模式(User mode)和内核模式(Kernal mode) 2. 用户模式的同步(CRITICAL_SECTION) 3. 内核模式同步 3.1 互斥量 3.2 信号量 3.3 事件对象 4. 实现Windows平台的多线程服务器端 1. 用户模式(User mode)和内核模式(Kernal mode) Windows操作系统的运行方式是“…...

JavaScript 基础第三天笔记

JavaScript 基础第三天笔记 if 多分支语句和 switch的区别&#xff1a; 共同点 都能实现多分支选择&#xff0c; 多选1大部分情况下可以互换 区别&#xff1a; switch…case语句通常处理case为比较确定值的情况&#xff0c;而if…else…语句更加灵活&#xff0c;通常用于范围…...

NebulaGraph实战:3-信息抽取构建知识图谱

自动信息抽取发展了几十年&#xff0c;虽然模型很多&#xff0c;但是泛化能力很难用满意来形容&#xff0c;直到LLM的诞生。虽然最终信息抽取质量部分还是需要专家审核&#xff0c;但是已经极大的提高了信息抽取的效率。因为传统方法需要大量时间来完成数据清洗、标注和训练&am…...

一百八十二、大数据离线数仓完整流程——步骤一、用Kettle从Kafka、MySQL等数据源采集数据然后写入HDFS

一、目的 经过6个月的奋斗&#xff0c;项目的离线数仓部分终于可以上线了&#xff0c;因此整理一下离线数仓的整个流程&#xff0c;既是大家提供一个案例经验&#xff0c;也是对自己近半年的工作进行一个总结。 二、项目背景 项目行业属于交通行业&#xff0c;因此数据具有很…...

工具篇 | H2数据库的使用和入门

引言 1.1 H2数据库概述 1.1.1 定义和特点 H2数据库是一款以 Java编写的轻量级关系型数据库。由于其小巧、灵活并且易于集成&#xff0c;H2经常被用作开发和测试环境中的便利数据库解决方案。除此之外&#xff0c;H2也适合作为生产环境中的嵌入式数据库。它不仅支持标准的SQL…...

PHP脚本导出MySQL数据库

背景&#xff1a;有时候需要同步数据库的表结构和部分数据&#xff0c;同步全表数据非常大&#xff0c;也不适合。还有一个种办法是使用数据库的dump命令执行备份&#xff0c;无法进入服务器&#xff1f;没有权限怎么办&#xff1f; 这里只要能访问服务器中的 information_sch…...

生成随机单据号

背景&#xff1a;全局生成4位字符2222-9ZZ9 实现方式&#xff1a; 使用redis的原子自增 google的retry保证&#xff0c;生成4位数 1、pom <dependency><groupId>com.github.rholder</groupId><artifactId>guava-retrying</artifactId><v…...

【计算机网络笔记五】应用层(二)HTTP报文

HTTP 报文格式 HTTP 协议的请求报文和响应报文的结构基本相同&#xff0c;由四部分组成&#xff1a; ① 起始行&#xff08;start line&#xff09;&#xff1a;描述请求或响应的基本信息&#xff1b;② 头部字段集合&#xff08;header&#xff09;&#xff1a;使用 key-valu…...

安装Python3.x--Windows

1 下载安装包 确定安装是干什么&#xff0c;要下哪个版本&#xff08;如果是配置项目环境&#xff0c;最好按项目需求的版本来装&#xff09; 1.1 官网链接 https://www.python.org 最新版本 指定版本 2 安装说明 点击下载exe&#xff0c;运行自定义安装路径&#xff0c;下…...

坐标休斯顿,TDengine 受邀参与第九届石油天然气数字化大会

美国中部时间 9 月 14 日至 15 日&#xff0c;第九届石油天然气数字化大会在美国德克萨斯州-休斯顿-希尔顿美洲酒店举办。本次大会汇聚了数百名全球石油天然气技术高管及众多极具创新性的数据技术方案商&#xff0c;组织了上百场硬核演讲&#xff0c;技术专家与行业从业者共聚一…...

labview 混合信号图 多曲线分组

如果你遇到了混合信号图 多曲线分组显示的问题&#xff0c;本文能给你帮助。 在文章的最好&#xff0c;列出了参考程序下载链接。 一个混合信号图中可包含多个绘图区域。 但一个绘图区域仅能显示数字曲线或者模拟曲线之一&#xff0c;无法兼有二者。 以下显示的分两组&#…...

客户端负载均衡_负载均衡策略

以前的Ribbon有多种负载均衡策略 RandomRule - 随性而为 解释&#xff1a; 随机 RoundRobinRule - 按部就班 解释&#xff1a; 轮询 RetryRule - 卷土重来 解释&#xff1a; 先按照RoundRobinRule的策略获取服务&#xff0c;如果获取服务失败则在指定时间内会进行重试。 Weigh…...

使用Python+Flask/Moco框架/Fiddler搭建简单的接口Mock服务

一、Mock测试 1、介绍 mock&#xff1a;就是对于一些难以构造的对象&#xff0c;使用虚拟的技术来实现测试的过程mock测试&#xff1a;在测试过程中&#xff0c;对于某些不容易构造或者不容易获取的对象&#xff0c;可以用一个虚拟的对象来代替的测试方法接口mock测试&#x…...

【Vue】Mock.js介绍和使用与首页导航栏左侧菜单搭建

目录 一、Mock.js 1.1 mockjs介绍 1.2 mock.js安装与配置 1.2.1 安装mock.js 1.2.2 引入mock.js 1.3 mock.js的使用 1.3.1 准备模拟数据 1.3.2 定义拦截路由 1.3.3 测试 二、首页导航栏左侧菜单搭建 2.1 自定义界面组件 (完整代码) 2.2 配置路由 2.3 组件显示折叠和…...

离散小波变换(概念与应用)

目录 概念光伏功率预测中,如何用离散小波变换提取高频特征概念 为您简单地绘制一些示意图来描述离散小波变换的基本概念。但请注意,这只是一个简化的示意图,可能不能完全捕捉到所有的细节和特性。 首先,我将为您绘制一个简单的小波函数和尺度函数的图像。然后,我会提供一…...

代码随想录day49:动态规划part10

121.买卖股票的最佳时机 贪心&#xff1a; class Solution { public:int maxProfit(vector<int>& prices) {int low INT_MAX;int result 0;for (int i 0; i < prices.size(); i) {low min(low, prices[i]); // 取最左最小价格result max(result, prices[i…...

fofa搜索使用

fofa搜索使用 文章目录 fofa搜索使用网站fofa搜索语法多条件查询 网站fofa https://fofa.info/搜索语法 1.title”beijing”从标题中搜索“北京2.headerQ"thinkphp”从http响应头中搜索“thinkphp3.body”管理后台”从html正文中搜索“管理后台4.domain”163.com”从子域…...

husky+lint-staged+eslint+prettier+stylelint+commitlint

概念: husky,暴露出git的hook钩子,在这些钩子执行一些命令,lint-staged,只在git的暂存区有修改的文件进行lint操作,执行一些校验脚本eslint,prettier,styelint有npm包还有对应的scode插件,其中npm包是用于执行那些诸如入eslint --fix "src/**/*.{js,jsx,…}"的脚本命…...

图像处理与计算机视觉--第四章-图像滤波与增强-第一部分

目录 1.灰度图亮度调整 2.图像模板匹配 3.图像裁剪处理 4.图像旋转处理 5.图像邻域与数据块处理 学习计算机视觉方向的几条经验: 1.学习计算机视觉一定不能操之过急&#xff0c;不然往往事倍功半&#xff01; 2.静下心来&#xff0c;理解每一个函数/算法的过程和精髓&…...

【go】字符串切片与字符串出入数据库转化

文章目录 需求代码入库出库 需求 将请求数据存入数据库与从数据库读取数据返回在出库不使用反序列化情况下 请求结构体 type NoticegroupsCreateReq struct {Name string json:"name" binding:"required"UserIds []string json:"user_ids…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看

文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...