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

C# 线程基础之 线程同步

线程同步的手段很多
lock 是通过内存索引块 0 1 切换 进行互斥的实现
互斥量 信号量 事件消息 其实意思就是 一个 标记量
通过这个标记 来进行类似的互斥手段

具体方式的分析 代码在后
1.互斥量 Mutex 作用 非常类似lock 一个Mutex 名称来代替 lock的引用对象
2.信号量 SemaphoreSlim 作用 同时访问同一个资源的线程数量 跨程序同步的场景下可以使用 混合模式
3.事件标识 AutoResetEvent 作用 类似lock 通过 waitone 阻塞与 set 通行 但只有一个线程可用标识
4.事件标识 ManualResetEventSlim 通过 set 通行 reset 设置阻塞 wait 阻塞 多个线程可用标识
5.事件标识 CountdownEvent 作用 限制指定次数信号量 达到后通行
6. SpinWait 自选锁 混合模式
7. ReaderWriterLockSlim 读写锁

互斥量 Mutex ReleaseMutex 测试先在项目路径cmd用命令行dotnet run 执行 然后在看断点

class Program
{static void Main(string[] args){const string MutexName = "testMutex";using (var m = new Mutex(false, MutexName)){if (!m.WaitOne(TimeSpan.FromSeconds(3), false)){Console.WriteLine("未捕获到互斥量");}else{Console.WriteLine("执行逻辑");Console.ReadLine();//释放互斥量m.ReleaseMutex();}}}
}

在这里插入图片描述

信号量 SemaphoreSlim 设置数量为4 循环中执行3次 主线程执行一次 发现主线程没执行
必须释放信号量 才可以 满足 最大执行数保存在4以内

class Program
{static void Main(string[] args){for (int i = 1; i <= 3; i++){string threadName = "Thread " + i;var t = new Thread(() => SemaphoreS(threadName, 2));t.Start();}var t1 = new Thread(() => SemaphoreS("4", 2));t1.Start();}static SemaphoreSlim _semaphore = new SemaphoreSlim(3);static void SemaphoreS(string name, int seconds){Console.WriteLine("{0} 开始", name);_semaphore.Wait();Console.WriteLine("{0} 执行", name);Thread.Sleep(TimeSpan.FromSeconds(seconds));Console.WriteLine("{0} 完成", name);//释放信号量//_semaphore.Release();}
}

在这里插入图片描述

AutoResetEvent 设置俩个 事件标识 通过set 畅通信号 waitone 阻塞信号
于是可以看到 子线程1》主线程2》子线程2 =子线程3》主线程3

class Program
{static void Main(string[] args){var t = new Thread(() => Process());t.Start();Console.WriteLine("主线程过程1");_workerEvent.WaitOne();Console.WriteLine("主线程过程2");_mainEvent.Set();_workerEvent.WaitOne();Console.WriteLine("主线程过程3");Console.ReadKey();}private static AutoResetEvent _workerEvent = new AutoResetEvent(false);private static AutoResetEvent _mainEvent = new AutoResetEvent(false);static void Process(){Console.WriteLine("子线程过程1");_workerEvent.Set();_mainEvent.WaitOne();Console.WriteLine("子线程过程2");Console.WriteLine("子线程过程3");_workerEvent.Set();}
}

在这里插入图片描述
但是如果使用多个子线程发现 其实只有1个子线程能够识别这个 标识
在这里插入图片描述

ManualResetEventSlim 为了解决上诉 1个线程使用标识的问题 俩个线程测试通过 主线程set通行
子线程中reset阻塞 并且wait等待 ManualResetEvent 用waitone

class Program
{static void Main(string[] args){var t = new Thread(() => Process(1));t.Start();var t1 = new Thread(() => Process(2));t1.Start();Console.WriteLine("主线程过程1");_mainEvent.Set();Thread.Sleep(3);Console.WriteLine("主线程过程2");_mainEvent.Set();Thread.Sleep(3);Console.WriteLine("主线程过程3"); _mainEvent.Set();Console.ReadKey();}private static ManualResetEvent _mainEvent = new ManualResetEvent(false);static void Process(int i){_mainEvent.WaitOne();Console.WriteLine(i+"子线程过程1");_mainEvent.Reset();_mainEvent.WaitOne();Console.WriteLine(i + "子线程过程2");_mainEvent.Reset();_mainEvent.WaitOne();Console.WriteLine(i + "子线程过程3");}
}
class Program
{static void Main(string[] args){var t = new Thread(() => Process(1));t.Start();var t1 = new Thread(() => Process(2));t1.Start();Console.WriteLine("主线程过程1");_mainEvent.Set();Thread.Sleep(3);Console.WriteLine("主线程过程2");_mainEvent.Set();Thread.Sleep(3);Console.WriteLine("主线程过程3"); _mainEvent.Set();Console.ReadKey();}private static ManualResetEventSlim _mainEvent = new ManualResetEventSlim(false);static void Process(int i){_mainEvent.Wait();Console.WriteLine(i+"子线程过程1");_mainEvent.Reset();_mainEvent.Wait();Console.WriteLine(i + "子线程过程2");_mainEvent.Reset();_mainEvent.Wait();Console.WriteLine(i + "子线程过程3");}
}

在这里插入图片描述
CountdownEvent 达到指定次数 继续通行 否则 wait阻塞 signal记录次数 Dispose 释放

class Program
{static void Main(string[] args){Console.WriteLine("俩个线程");var t1 = new Thread(() => StartThread("线程1", 4));var t2 = new Thread(() => StartThread("线程2", 8));t1.Start();t2.Start();_countdown.Wait();Console.WriteLine("结束");_countdown.Dispose();Console.ReadKey();}static CountdownEvent _countdown = new CountdownEvent(2);//指定次数 如果设置3 那么 结束不会打印一直会阻塞static void StartThread(string message, int seconds){Thread.Sleep(TimeSpan.FromSeconds(seconds));Console.WriteLine(message);_countdown.Signal();//记录次数执行一次就会+1 如果不设置 那么不达到指定次数 也会一直 wait阻塞}
}

在这里插入图片描述SpinWait 自选锁 混合模式 用户模式 短时间循环等待 然后行不通即切换上下文 内核模式阻塞

class Program
{static void Main(string[] args){var t1 = new Thread(UserModeWait);//用户模式var t2 = new Thread(HybridSpinWait);//混合模式Console.WriteLine("用户模式");t1.Start();Thread.Sleep(1);_isOKUser = true;Console.WriteLine("混合模式");t2.Start();Thread.Sleep(5);_isOKSpin = true;Console.ReadKey();}static volatile bool _isOKUser = false; //volatile 修饰多个线程访问 最新值 存在内存中static volatile bool _isOKSpin = false;static void UserModeWait(){while (!_isOKUser){Console.Write("-不ok-");}Console.WriteLine();Console.WriteLine("用户模式OK");}static void HybridSpinWait(){var w = new SpinWait(); //先用户模式占用cpu资源等待循环9次 若还没等到执行通行 那么切换上下文 内核模式 阻塞 while (!_isOKSpin){w.SpinOnce();Console.WriteLine("混合模式上下文切换"+w.NextSpinWillYield);}Console.WriteLine("混合模式OK");}
}

在这里插入图片描述

ReaderWriterLockSlim 读写锁 就是 读可共享锁 写是互斥锁 当断点设置在写锁的过程中 可以看见 读取的时候被阻塞了

class Program
{static void Main(string[] args){new Thread(Read) { IsBackground = true }.Start();new Thread(Read) { IsBackground = true }.Start();new Thread(Read) { IsBackground = true }.Start();new Thread(() => Write("Thread 1")) { IsBackground = true }.Start();new Thread(() => Write("Thread 2")) { IsBackground = true }.Start();Thread.Sleep(TimeSpan.FromSeconds(10));Console.ReadKey();}static ReaderWriterLockSlim _rw = new ReaderWriterLockSlim();static Dictionary<int, int> _items = new Dictionary<int, int>();static void Read(){Console.WriteLine("读取");while (true){try{_rw.EnterReadLock();foreach (var key in _items.Keys){Console.WriteLine(Thread.CurrentThread.ManagedThreadId+ "读取字典"+key);Thread.Sleep(TimeSpan.FromSeconds(0.1));}}finally{_rw.ExitReadLock();}}}static void Write(string threadName){while (true){try{int newKey = new Random().Next(5);_rw.EnterUpgradeableReadLock();if (!_items.ContainsKey(newKey)){try{_rw.EnterWriteLock();_items[newKey] = 1;Console.WriteLine("{0}添加字典 {1}", threadName, newKey);}finally{_rw.ExitWriteLock();}}Thread.Sleep(TimeSpan.FromSeconds(0.1));}finally{_rw.ExitUpgradeableReadLock();}}}
}

在这里插入图片描述

相关文章:

C# 线程基础之 线程同步

线程同步的手段很多 lock 是通过内存索引块 0 1 切换 进行互斥的实现 互斥量 信号量 事件消息 其实意思就是 一个 标记量 通过这个标记 来进行类似的互斥手段 具体方式的分析 代码在后 1.互斥量 Mutex 作用 非常类似lock 一个Mutex 名称来代替 lock的引用对象 2.信号量 Semaph…...

[c语言日寄]c语言也有“回”字的多种写法——整数交换的三种方式

大家好啊&#xff0c;在今天的快乐刷题中&#xff0c;我们遇到了这样一道题目&#xff1a; 题目 写出 三种不同方式的 交换两个整数变量的 函数 交换变量的三种解法 常规方式 想要交换两个变量很简单&#xff0c;第一种方式就是新建一个临时变量&#xff0c;具体流程如下&…...

RocketMQ 知识速览

文章目录 一、消息队列对比二、RocketMQ 基础1. 消息模型2. 技术架构3. 消息类型4. 消费者类型5. 消费者分组和生产者分组 三、RocketMQ 高级1. 如何解决顺序消费和重复消费2. 如何实现分布式事务3. 如何解决消息堆积问题4. 如何保证高性能读写5. 刷盘机制 &#xff08;topic 模…...

优化 Azure Synapse Dedicated SQL Pool中的 SQL 执行性能的经验方法

在 Azure Synapse Dedicated SQL Pool中优化 SQL 执行涉及了解底层体系结构&#xff08;例如分布和分区&#xff09;、查询优化&#xff08;例如避免不必要的子查询和联接&#xff09;&#xff0c;以及利用具体化视图和 PolyBase 等工具进行高效数据加载。 1.有效使用分布和分…...

详解英语单词“pro bono”:公益服务的表达(中英双语)

中文版 详解英语单词“pro bono”&#xff1a;公益服务的表达 一、词义解释 “Pro bono” 是一个源自拉丁语的短语&#xff0c;完整表达为 “pro bono publico”&#xff0c;意思是“为了公众利益”&#xff08;for the public good&#xff09;。在现代英语中&#xff0c;它…...

16. C语言 字符串详解

本章目录: 前言C 字符串的基础概念字符串的定义字符串的内存表示 常见的字符串操作函数示例代码 深入探讨字符串长度计算strlen 与 sizeof 的区别 字符串操作的注意事项**1. 字符数组的大小**2. 字符数组和字符指针的区别3. 使用安全函数 字符串的遍历与格式化输出**遍历字符串…...

使用Buildroot开始嵌入式Linux系统之旅-3

文章目录 at91bootstrap操作教程修改at91bootstrap具体配置重新编译at91bootstrap U-Boot操作教程修改U-Boot具体配置重新编译U-Boot Linux Kernel操作教程修改Linux Kernel具体配置重新编译Linux Kernel buildroot操作进阶生成图形化软件模块依赖关系查看具体软件模块依赖关系…...

[免费]SpringBoot+Vue新能源汽车充电桩管理系统【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringBootVue新能源汽车充电桩管理系统&#xff0c;分享下哈。 项目视频演示 【免费】SpringBootVue新能源汽车充电桩管理系统 Java毕业设计_哔哩哔哩_bilibili 项目介绍 随着信息化时代的到来&#xff0…...

【已解决】【记录】2AI大模型web UI使用tips 本地

docker desktop使用 互动 如果需要发送网页链接&#xff0c;就在链接上加上【#】号 如果要上传文件就点击这个➕号 中文回复 命令它只用中文回复&#xff0c;在右上角打开【对话高级设置】 输入提示词&#xff08;提示词使用英文会更好&#xff09; Must reply to the us…...

44.ComboBox的数据绑定 C#例子 WPF例子

固定最简步骤&#xff0c;包括 XAML&#xff1a; 题头里引入命名空间 标题下面引入类 combobox绑定资源属性和选择属性&#xff0c;block则绑定和combobox一样的选择属性 C#&#xff1a; 通知的类&#xff0c;及对应固定的任务 引入字段 引入属性 其中资源是只读的 选…...

物联网之传感器技术

引言 在数字化浪潮席卷全球的今天&#xff0c;物联网&#xff08;IoT&#xff09;已成为推动各行各业变革的重要力量。而物联网传感器&#xff0c;作为物联网感知层的核心技术&#xff0c;更是扮演着不可或缺的角色。它们如同人类的五官&#xff0c;能够感知物理世界中的各种信…...

QTreeWidget QTreeWidgetItem

QTreeWidgetItem 是 Qt 框架中用于在 QTreeWidget 中表示树形结构中每个节点的类。它是 QTreeWidget 的一部分&#xff0c;允许您创建和管理层次结构的数据展示。 QTreeWidgetItem 用于表示树形结构中的单个节点。 添加子节点&#xff1a; 可以通过 addChild() 方法向节点添加…...

torch.einsum计算张量的外积

torch.einsum 是一种强大的张量操作工具,可以通过爱因斯坦求和约定(Einstein summation convention)来简洁地表示复杂的张量运算。通过它,我们可以高效地计算矩阵乘法、转置、点积、外积等操作。 以下是关于如何使用 torch.einsum 计算两个四维张量在第三维度上的外积的解…...

PostgreSQL 超级管理员详解

1. 什么是 PostgreSQL 超级管理员 PostgreSQL 超级管理员&#xff08;superuser&#xff09;是拥有数据库系统最高权限的用户。他们可以执行任何数据库操作&#xff0c;包括但不限于创建和删除数据库、用户、表空间、模式等。超级管理员权限是 PostgreSQL 中权限的最高级别。 …...

RabbitMQ 工作模式使用案例之(发布订阅模式、路由模式、通配符模式)

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;RabbitMQ &#x1f4da;本系列文章为个人学…...

【2024年华为OD机试】(C卷,100分)- 机场航班调度程序 (Java JS PythonC/C++)

一、问题描述 题目描述 XX市机场停放了多架飞机&#xff0c;每架飞机都有自己的航班号&#xff0c;如CA3385&#xff0c;CZ6678&#xff0c;SC6508等&#xff0c;航班号的前2个大写字母&#xff08;或数字&#xff09;代表航空公司的缩写&#xff0c;后面4个数字代表航班信息…...

Vue.js组件开发-使用地图绘制轨迹

在Vue.js中开发一个组件来展示地图并绘制轨迹&#xff0c;可以使用诸如Leaflet.js、Mapbox GL JS或百度地图等地图库。这些库提供了丰富的API来创建和定制地图&#xff0c;以及绘制路径、标记和其他地图元素。 示例&#xff1a; 1. 安装Leaflet.js 首先&#xff0c;需要安装…...

vue 与 vue-json-viewer 实现 JSON 数据可视化

前言 接口的调试和测试是确保系统稳定性的重要步骤。为了让开发人员和测试人员能够直观地查看接口返回的 JSON 数据&#xff0c;使用合适的工具至关重要。vue-json-viewer 插件为 vue 开发者提供了一个简单而强大的解决方案。本文将详细介绍如何在 vue 项目中使用该插件&#x…...

ubuntu Android : adb logcat 过滤多个log

指定字符串的log&#xff0c;可以用下面的形式&#xff0c;注意加-E和单引号&#xff1a; adb shell " logcat | grep -E strings1|strings2 " 参考&#xff1a;Android : adb logcat 过滤多个log 用adb shell “ logcat | grep -E ‘strings1| strings2 ‘ “ 形…...

kubeneters-循序渐进Cilium网络(三)

文章目录 概要命名空间之间的路由同一节点上的 Pod 到 Pod 路由跨节点的 Pod 间路由总结 概要 在前一篇讨论网络接口的内容中&#xff0c;详细分析了如何识别所有参与 Pod 间路由的接口。同时&#xff0c;以简明的非技术语言阐述了 Cilium 在 Kubernetes 集群中的路由机制。接…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...