.Net SemaphoreSlim
看Elsa-core源代码中看到的,Elsa-core中所有保存数据的方法似乎使用同一个Save方法。如下图:

那么为什么要使用这玩意,我还是头一次见这玩意
????
好吧,我承认我自己菜。我自个儿也该保持谦虚态度学习学习了。
先看下这个SemaphoreSlim类的描述

(我买的正版Resharp 2022)反编译代码如上图。
翻译过来就是 “限制当前访问资源或池中资源的线程数”,真的是这样的吗? 试一试...
我的代码仓库 https://github.com/qiqiqiyaya/Learning-Case/tree/main/SemaphoreSlim ,测试例子。
例一
在B乎中看到一篇文章 ,链接地址 https://zhuanlan.zhihu.com/p/158777952
我copy他的例子测试了下,如下图:
// 现在有10个人要过桥
// 但是一座桥上只能承受5个人,再多桥就会塌
static void SemaphoreTest()
{
var semaphore = new SemaphoreSlim(5);
for (int i = 1; i <= 10; i++)
{
Thread.Sleep(100); // 排队上桥
var index = i; // 定义index 避免出现闭包的问题
Task.Run(() =>
{
semaphore.Wait();
try
{
Console.WriteLine($"第{index}个人正在过桥。");
Thread.Sleep(5000); // 模拟过桥需要花费的时间
}
finally
{
Console.WriteLine($"第{index}个人已经过桥。");
semaphore.Release();
}
});
}
}

运行结果,与B乎上作者的结果一样。
这里有个问题,该作者使用Task.Run 然后在其中添加了 Thread.Sleep(5000); ,这会阻塞当前的线程,导致执行 Task 任务的“任务调度器”开启了 10个线程。
关于什么是 Task ,理解 Task ,请阅读大佬的文章,非常Nice链接 https://www.cnblogs.com/artech/p/task_scheduling.html
列二,如果在异步Async/Await中是什么情况呢?
static void SemaphoreTest1()
{
var semaphore = new SemaphoreSlim(5);
for (int i = 1; i <= 10; i++)
{
Thread.Sleep(100); // 排队上桥
var index = i; // 定义index 避免出现闭包的问题
Task.Run(async () =>
{
Console.WriteLine($"第{index}个人已抵达桥边上。线程Id " + Thread.CurrentThread.ManagedThreadId);
semaphore.Wait();
try
{
Console.WriteLine($"第{index}个人正在过桥。线程Id " + Thread.CurrentThread.ManagedThreadId);
//Thread.Sleep(5000); // 模拟过桥需要花费的时间
await Task.Delay(5000);
}
finally
{
Console.WriteLine($"第{index}个人已经过桥。线程Id " + Thread.CurrentThread.ManagedThreadId);
semaphore.Release();
}
});
}
}
将 Thread.Sleep(5000); 换成了 await Task.Delay(5000); 使用 async/await 。结果如下:

从结果上看,例如:第1个人 线程Id 3 上桥,在执行 await Task.Delay(5000); (模拟过桥需要花费的时间)后,线程Id变成了 12 。这是执行 Task 的任务调度器(Task默认调度器是线程池)作用的效果。这不是本次随笔的重点。
再次强调关于理解Task,请参考 https://www.cnblogs.com/artech/p/task_scheduling.html 。
再次强调Task与线程Thread是两个东西,Task可以理解为一个任务,那么这个任务由Thread去执行,至于由哪一个Thread 去执行,这就由 “任务调度器” 去决定了。
从结果上看,执行了五个Task之后,就阻止后续Task再往下执行代码了。
列二中使用了 semaphore.Wait(); 同步阻塞 , 导致后续Task中使用一个之前空闲的Id为3的线程 (Id为3的线程执行到 await Task.Delay(5000) 后阻塞了,该线程被调度器拿去执行新的Task任务),并创建了另外4个新的执行Task的线程。
结论:
1.SemaphoreSlim会限制访问资源的线程数
2.在异步async/await情况下,SemaphoreSlim会限制正在执行访问资源的Task的数量
例三
static void SemaphoreTest2()
{
var semaphore = new SemaphoreSlim(5);
for (int i = 1; i <= 10; i++)
{
Thread.Sleep(100); // 排队上桥
var index = i; // 定义index 避免出现闭包的问题
Task.Run(async () =>
{
Console.WriteLine($"第{index}个人已抵达桥边上。线程Id " + Thread.CurrentThread.ManagedThreadId);
await semaphore.WaitAsync();
try
{
Console.WriteLine($"第{index}个人正在过桥。线程Id " + Thread.CurrentThread.ManagedThreadId);
await Task.Delay(5000);// 模拟过桥需要花费的时间
}
finally
{
Console.WriteLine($"第{index}个人已经过桥。线程Id " + Thread.CurrentThread.ManagedThreadId);
semaphore.Release();
}
});
}
}
执行结果

例三中我使用了 await semaphore.WaitAsync(); ,也就是说不存在同步阻塞代码了,全部都是异步。
异步下遇到await,执行等待,那么当前执行这个Task的线程可能会被“任务调度器拿去执行别的Task”
结论:
1.在异步async/await情况下,SemaphoreSlim会限制正在执行访问资源的Task的数量, 与例二第二个结论一致。
异步下的Lock锁
鉴于SemaphoreSlim的特殊性,可以使用其通过程序角度来实现“锁”(叫乐观锁、还是悲观锁???,我去,两者好像都不是
)
乐观锁与悲观锁定义如下图

如下图代码:
重点代码 SemaphoreSlim _semaphore = new(1)
如果真要说明,我觉得是最多像 悲观锁 ,但又不全是。这里的SemaphoreSlim只是说同一时刻只准一个线程或正在执行的Task做保存操作,那么如果是分布式程序呢,另一个程序此时也执行保存操作?
我觉得其真实原因是:使用SemaphoreSlim可以实现异步下的 Lock 锁。


.Net SemaphoreSlim的更多相关文章
- 【C#】【Thread】Semaphore/SemaphoreSlim信号量
System.Threading.Semaphore 类表示一个命名(系统范围)信号量或本地信号量. 它是一个对 Win32 信号量对象的精简包装. Win32 信号量是计数信号量,可用于控制对资源池 ...
- C#并行编程 (Barrier,CountdownEvent,ManualResetEventSlim,SemaphoreSlim,SpinLock,SpinWait )
背景 有时候必须访问变量.实例.方法.属性或者结构体,而这些并没有准备好用于并发访问,或者有时候需要执行部分代码,而这些代码必须单独运行,这是不得不通过将任务分解的方式让它们独立运行. 当任务和线程要 ...
- 第十三节:实际开发中使用最多的监视锁Monitor、lock语法糖的扩展、混合锁的使用(ManualResetEvent、SemaphoreSlim、ReaderWriterLockSlim)
一. 监视锁(Monitor和lock) 1. Monitor类,限定线程个数的一把锁,两个核心方法: Enter:锁住某个资源. Exit:退出某一个资源. 测试案例:开启5个线程同时对一个变量进行 ...
- C# SemaphoreSlim 实现
当多个任务或线程并行运行时,难以避免的对某些有限的资源进行并发的访问.可以考虑使用信号量来进行这方面的控制(System.Threading.Semaphore)是表示一个Windows内核的信号量对 ...
- 《C#多线程编程实战》2.4 SemaphoreSlim
这个简单多了. 理解也是很好理解. 比上一个mutex好理解多了. 这个SemaphoreSlim是干什么呢? 就是限制线程的来访问. 好比说一次只有两个,一次只有三个 这样的线程来访问资源. 有点 ...
- Semaphore and SemaphoreSlim
https://msdn.microsoft.com/en-us/library/z6zx288a(v=vs.110).aspx The System.Threading.Semaphore clas ...
- SemaphoreSlim
https://msdn.microsoft.com/en-us/library/system.threading.semaphoreslim(v=vs.110).aspx Represents a ...
- 一次 .NET Core 中玩锁的经历:ManualResetEventSlim, Semaphore 与 SemaphoreSlim
最近同事对 .net core memcached 缓存客户端 EnyimMemcachedCore 进行了高并发下的压力测试,发现在 linux 上高并发下使用 async 异步方法读取缓存数据会 ...
- Mutex vs Semaphore vs Monitor vs SemaphoreSlim
C#开发者(面试者)都会遇到Mutex,Semaphore,Monitor,SemaphoreSlim这四个与锁相关的C#类型,本文期望以最简洁明了的方式阐述四种对象的区别. 线程安全 教条式理解 如 ...
- SemaphoreSlim 实现
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/dz45693/article/deta ...
随机推荐
- 六 抽象工厂模式【Abstract Factory Pattern】 来自CBF4LIFE 的设计模式0
好了,我们继续上一节课,上一节讲到女娲造人,人是造出来了,世界时热闹了,可是低头一看,都是清一色的类型,缺少关爱.仇恨.喜怒哀乐等情绪,人类的生命太平淡了,女娲一想,猛然一拍脑袋,Shit!忘记给人类 ...
- 第三十二篇:vue的响应式原理
好家伙 什么是响应式?比较官方的回答: Vue.js 的核心包括一套"响应式系统". "响应式",是指当数据改变后,Vue 会通知到使用该数据的代码. 例如,视 ...
- Haproxy部署及控制台使用手册
一.介绍 1.简介 HAProxy是一个使用C语言编写开源软件,提供高可用,负载均衡,以及基于TCP(四层)和HTTP(七层)的应用程序代理: HAProxy特别适用于那些负载特大的web站点,这些站 ...
- MySQL到底有没有解决幻读问题?这篇文章彻底给你解答
MySQL InnoDB引擎在Repeatable Read(可重复读)隔离级别下,到底有没有解决幻读的问题? 网上众说纷纭,有的说解决了,有的说没解决,甚至有些大v的意见都无法达成统一. 今天就深入 ...
- TDengine概述以及架构模型
TDengine TDengine是一个高效的存储.查询.分析时序大数据的平台,专为物联网.车联网.工业互联网.运维监测等优化而设计. 您可以像使用关系型数据库MySQL一样来使用它. TDengin ...
- YAML资源清单
YAML 文件基本语法格式 在 Docker 环境下面我们是直接通过命令 docker run 来运行我们的应用的,在 Kubernetes 环境下面我们同样也可以用类似 kubectl run 这样 ...
- 30分钟掌握 Webpack
本文基于:峰华前端工程师--30分钟掌握Webpack 为什么使用 Webpack 在我们进行传统网页开发中,会在 index.html 中引入大量的 js 和 css 文件,不仅可能会导致命名冲突, ...
- 企业MES系统与ERP信息集成要素有哪些?
关于要讲明企业MES系统与ERP信息集成要素有哪些,得先弄清楚他们之间的关系:从工厂的管理来说,ERP在上MES在下,ERP统领企业全局包括MES,为管理层服务,重心在于企业决策,ERP对企业宏观管理 ...
- Ubuntu转到root用户后用户名路径没有颜色
看到好多都直接把普通会用户的.bashrc配置复制到root家目录下,由于本人小白,很多.bashrc配置看不懂,也不敢随便修改.于是找到别的配置方法. 普通用户的os@a:~$和root用户的roo ...
- 关于aws cli命令的exit/return code分析
最近总是收到一个备份脚本的失败邮件,脚本是之前同事写的,没有加入任何有调试信息,及有用的日志 于是去分析 ,脚本中有一条 aws s3 sync $srclocal $dsts3 命令,然后根据这条 ...