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

news/2023/11/29 6:46:16

目录

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平台的多线程服务器端


http://www.ppmy.cn/news/1124804.html

相关文章

Flink-CDC——MySQL、SqlSqlServer、Oracle等数据库开启日志方法

文章目录 1. 前言 2. 数据源安装与配置 2.1 MySQL 2.1.1 安装 2.1.2 CDC 配置 2.2 Postgresql 2.2.1 安装 2.2.2 CDC 配置 2.3 Oracle 2.3.1 安装 2.3.2 CDC 配置 2.4 SQLServer 2.4.1 安装 2.4.2 CDC 配置 3. 验证 3.1 Flink版本与CDC版本的对应关系 3.2 下载…

MySQL索引失效场景,索引优化,索引原理

文章主要有以下内容&#xff1a;MySQL性能优化、索引优化、索引失效&#xff0c;索引原理、索引分类。 索引是一个用于提高数据库查询速度的数据结构。不必搜索整个数据库&#xff0c;通过使用索引&#xff0c;数据库系统可以直接找到存储在磁盘上的特定行的地址。没有索引&am…

题目 1060: 二级C语言-同因查找

题目描述 求出10至1000之内能同时被2、3、7整除的数&#xff0c;并输出。 每行一个。 这题思路就像行云流水&#xff0c;可以一气呵成。 for循环10到1&#xff0c;000的数&#xff0c;if判断如果同时被2、3、7整除就输出 #include<iostream> using namespace std;//求…

基于R语言分位数回归丨线性回归假设与分位数函数、线性分位数回归 、贝叶斯分位数回归、超越线性分位数回归等

目录 专题一 线性回归假设与分位数函数讲解 专题二 线性分位数回归 【代码实践】 专题三 贝叶斯分位数回归【代码实践】 专题四 超越线性分位数回归&#xff08;一&#xff09;【代码实践】 专题五 超越线性分位数回归&#xff08;二&#xff09;【代码实践】 更多应用 回…

PostgreSql 统一修改date字段为timestamp

在《Powdersigner PostgreSql 同步表结构到pg数据库》中&#xff0c;导入表结构到pg数据后&#xff0c;发下时间对不上了。mysql的datetime转换后pg的变成了date了。 再同步到数据后&#xff0c;就变成日期类型了。 因为表中基本都有创建时间和修改时间&#xff0c;两个相对固…

【算法】算法设计与分析 课程笔记 第二章 递归与分治策略

2.1 递归 直接或间接地调用自身的算法称为递归算法。 用函数自身给出定义的函数称为递归函数。 2.1.1 阶乘 首先得想到一个求阶乘的函数&#xff1a; 这个函数的下面那个式子就用到了调用自身&#xff0c;所以可以用递归来实现&#xff0c;将主问题拆分成若干层的子问题&am…

二进制中1的个数 C++实现

题目&#xff1a; 代码&#xff1a; #include<iostream> using namespace std; const int N100010; int a[N]; int n;int lowbit(int x){return x & -x; }int main(){scanf("%d",&n);for(int i0;i<n;i) scanf("%d",&a[i]);for(int i…

Spring面试题9:Spring的BeanFactory和FactoryBean的区别和联系

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:说一说Spring的BeanFactory和FactoryBean的区别和联系 区别:BeanFactory是一个工厂接口,主要负责管理和创建Bean实例。它是Spring提供的最底层的…

Level FHE 的高效实现 兼容 Level FHE 的高级算法

参考文献&#xff1a; [CS05] Choi Y, Swartzlander E E. Parallel prefix adder design with matrix representation[C]//17th IEEE Symposium on Computer Arithmetic (ARITH’05). IEEE, 2005: 90-98.[SV11] Smart N P, Vercauteren F. Fully homomorphic SIMD operations[…

二、ubuntu主机端tftp及nfs服务开发环境安装

一.主机端tftp服务环境安装及配置 检查是否已经安装tftp server $dpkg -s tftpd-hpa#如果提示未安装服务&#xff0c;则执行下面安装指令$sudo apt-get install tftpd-hpa tftp-hpa#tftpd-hpa服务端 tftp-hpa客户端创建tftp启动目录&#xff0c;用于存放内核与设备树文件&a…

Android跨进程通信:Binder机制原理

目录 1. Binder到底是什么&#xff1f; 2. 知识储备 2.1 进程空间划分 2.2 进程隔离 & 跨进程通信&#xff08; IPC &#xff09; 2.3 内存映射 2.3.1 作用 2.3.2 实现过程 2.3.3 特点 2.3.4 应用场景 2.3.5 实例讲解 ① 文件读 / 写操作 ② 跨进程通信 3. Bi…

企业资源计划即 ERP (Enterprise Resource Planning)

ERP是一种主要面向制造行业进行物质资源、资金资源和信息资源集成一体化管理的企业信息管理系统。ERP是一个以管理会计为核心可以提供跨地区、跨部门、甚至跨公司整合实时信息的企业管理软件。针对物资资源管理&#xff08;物流&#xff09;、人力资源管理&#xff08;人流&…

java面试题-jvm基础知识

1 JVM组成 1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f; 难易程度&#xff1a;☆☆☆ 出现频率&#xff1a;☆☆☆☆ JVM是什么 Java Virtual Machine Java程序的运行环境&#xff08;java二进制字节码的运行环境&#xff09; 好处&#xff1a; 一次编写&a…

顺序读写函数的介绍:fputs fgets

目录 函数介绍&#xff1a; fputs&#xff1a; 写入多行字符到文件中&#xff1a; 文件效果&#xff1a; 图中的效果是变成了一行&#xff0c;那么想要变成多行的效果应该如下代码所示进行操作&#xff1a; 多行字符代码&#xff1a; 文件效果&#xff1a; fgets&#…

Visual Studio 更新:远程文件管理器

Visual Studio 中的远程文件管理器可以用来访问远程机器上的文件和文件夹&#xff0c;通过 Visual Studio 自带的连接管理器&#xff0c;可以实现不离开开发环境直接访问远程系统&#xff0c;这确实十分方便。 自从此功能发布以来&#xff0c;VS 开发团队努力工作&#xff0c;…

Supervisor进程管理

Supervisor进程管理 概述&#xff1a;supervisor 是一个用 python 语言编写的进程管理工具&#xff0c;它可以很方便的监听、启动、停止、重启一个或多个进程。当一个进程意外被杀死&#xff0c;supervisor 监听到进程死后&#xff0c;可以很方便的让进程自动恢复&#xff0c;…

前端项目练习(练习-004-webpack-02)

学习前&#xff0c;首先&#xff0c;创建一个web-004项目&#xff0c;内容和web-003一样。&#xff08;注意将package.json中的name改为web-004&#xff09; 前面的例子&#xff0c;成功将js文件打包到了dist中&#xff0c;但是我们有三个文件&#xff0c;css&#xff0c;js和h…

Java基于SSM+JSP的服装定制系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 1 简介2 .技术栈3 分析4系统设计4.1 软件功能模块设计4.2.2 物理模型设计 5系统详细设计5.1系统功…

数据链路层协议

文章目录 数据链路层协议0. 数据链路层解决的问题1. 以太网协议(1) 认识以太网(2) 以太网帧格式<1> 两个核心问题 (3) 认识MAC地址(4) 局域网通信原理(5) MTU<1> 认识MTU<2> MTU对IP协议的影响<3> MTU对UDP协议的影响<4> MTU对TCP协议的影响<…

抽检监测实施

声明 本文是学习GB-T 42893-2023 电子商务交易产品质量监测实施指南. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件提供了开展电子商务交易的有形产品质量监测的总则&#xff0c;监测准备、监测实施、监测效果评价 与反馈等过程指导…
最新文章