c#之信号量Semaphore

学习目标:

在工作中用到信号量,记录一下


一、概念

Semaphore 一般译作 信号量,它也是一种线程同步工具,主要用于多个线程对共享资源进行并行操作的一种工具类。它代表了一种许可的概念,是否允许多线程对同一资源进行操作的许可,使用 Semaphore 可以控制并发访问资源的线程个数。

注意本片引入一个比喻;信号量就是你要去一个房间但是有一个门卫在房间门口检查;

二、基础使用

2.1初始化 public Semaphore(int initialCount, int maximumCount);

参数 1:initialCount 初始可用资源数

参数initialCount是设置默认最多可以有几个访问,也是设置初始有多少;比如你设置2,那就是最开始就有2个资源可以用可以去访问,不用等释放;但是最多也是只有同时2个访问;也就是一开始门卫就允许2个人进去,而且也规定了门卫一次只允许2个人进去房间,

参数 2:maximumCount 最大资源上限

  1. 含义:信号量计数器能达到的最大值,封顶值
  2. 硬性规则: 每次调用 Release(n) 会把计数器 +n; 如果相加后 新计数 > maximumCount → 直接抛出 SemaphoreFullException 报错。
  3. 作用:限流保护,防止无限释放资源导致并发失控

比如你设置了5,信号量计数器能达到的最大值最多就是只能是5;也就是门卫只允许最多同时有5个人在房间附近而且包括了在房间里的;你得从房间走了1个才能再来一个;

2.2 阻塞,等待允许访问的信号量 s.WaitOne()

WaitOne()其实是做减法;一条指令就是减一;就相当于门卫一次只允许同时两个人进去,进去一个人就少一个名额;名额减少到了0那就要排队等;

2.3 s.Release(int x) 释放资源

这个相当于叫人来排队;但是要注意门卫的可接受最多人数;比如设置了(3,7),已经有3个人在访问了,在这三个人干完活之后,这时候只能最多再叫3个人来排队,理论上来说就是s.WaitOne()多少就s.Release()多少

三、使用场景

信号量有两种使用场景,一种是并发限流也就是控制多少个线程可以访问一个方法;还有是生产者消费者任务通知;

3.1 并发限流

多线程抢固定名额,WaitOne 和 Release 必须成对,基础格式如下:

  1. WaitOne():占用 1 个名额,计数器 -1
  2. 执行需要限流的耗时逻辑
  3. Release(1):归还名额,计数器 +1

然后多个线程一起执行这个方法,但是因为规定了最多是5,所以虽然创建了一百个但是制最多只能有五个线程执行,之后要等一个线程执行完了才到下一个;

cs 复制代码
Semaphore s = new Semaphore(5,5);
void TaskWork()
{
    s.WaitOne(); // 抢名额
    try
    {
        // 并发受限的业务代码
        Thread.Sleep(2000);
    }
    finally
    {
        s.Release(1); // 用完必须归还
    }
}


/////////////////////
 int p = 0;
            Thread x;
            for(int i=0;i<100;i++)
            {
                x = new Thread(TaskWork);
                x.Start();
               
            }

3.2 生产者消费者任务通知

生产者放任务 Release,消费者取任务 WaitOne,消费者不 Release

  1. 生产者(addTaskThread / 界面按钮):往队列加任务 → Release(n),计数器 +n
  2. 消费者(唯一通信线程):WaitOne() 计数器 -n,取出任务执行
  3. 消费者执行完任务不归还资源

关键特征

  1. 不对称:生产者 Release,消费者只 Wait 不 Release;
  2. 全程只有1 个消费线程,不存在并发控制;
  3. initialCount 固定 0,启动无任务直接阻塞;
  4. maximumCount 是任务堆积上限,不是线程上限。
cs 复制代码
// 生产者:新增任务
lock(lockObj)
{
    queue.Enqueue(task);
}
_TaskSemaphore.Release(1);

// 消费者(单一线程循环)
Task.Run(()=>{
while(true)
{
    _TaskSemaphore.WaitOne();
    lock(lockObj)
    {
        var task = queue.Dequeue();
    }
    // 执行Modbus通信,没有Release
}
});

3.3 特殊的一种

一个按钮就是干活,但是点了超过5次就不会输入ok,只有点击另外一个按钮Release一个才会继续输出ok;

cs 复制代码
Semaphore s = new Semaphore(5,5);
        private void button4_Click(object sender, EventArgs e)
        {
            Task.Run(() =>
            {
                s.WaitOne();
                Console.WriteLine("ok");
            }
            );
           
        }

        private void button5_Click(object sender, EventArgs e)
        {
            s.Release(1);
        }

学习时间:

26.07.13


相关推荐
kdxiaojie1 小时前
Linux 驱动研究 —— I2C (5)
linux·运维·笔记·学习
会周易的程序员2 小时前
从零构建多核CPU负载自适应控制系统
linux·c++·笔记·物联网·测试工具
茯苓gao3 小时前
机器人产业的三级火箭:卖电机、卖模组与卖整机,三种商业模式的终局推演
笔记·学习·机器人
LuminousCPP3 小时前
C 语言集中实践全记录:顺序表 + 单链表原理实现与通讯录项目实战【附可运行源码】
c语言·开发语言·数据结构·经验分享·笔记
马里马里奥-4 小时前
大模型应用开发笔记04:LangChain 工具(Tool)与中间件(Middleware)详解
笔记·中间件·langchain
乐橙开放平台5 小时前
巡店系统笔记:乐橙 listDeviceDetailsByPage 台账 + bindDeviceLive 辅码流最小闭环
网络·笔记·物联网·自动化·音视频·智能家居
LuminousCPP6 小时前
单链表专题(应用篇):三道经典题吃透删除、反转与快慢指针
c语言·数据结构·笔记
女神下凡7 小时前
WorkBuddy、TraeWork、QWork 完整区别对比
ide·人工智能·笔记
疯狂打码的少年7 小时前
【软件工程】需求工程:需求规格说明与需求验证
笔记·软件工程