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


相关推荐
蒸蒸yyyyzwd2 小时前
从面经中学到的三件事
c++·笔记
IT小白杨4 小时前
社媒多账号运营的环境隔离:一张图谱模型,解释清“为什么换IP还是被连坐“
经验分享·笔记·矩阵·指纹浏览器
PC2005-cloud7 小时前
Rust学习笔记:控制流——if、loop、while、for与match
笔记·学习·rust
IT笔记7 小时前
Rust 迭代器多级链式调用执行顺序笔记
开发语言·笔记·rust
kyrie_sakura8 小时前
python学习笔记14 -- FastAPI
笔记·python·学习·fastapi
sunoo-22910 小时前
【51单片机实战】温控下位机完整实现:DS18B20采集 + 自定义二进制串口协议 + PWM声光告
笔记·学习·51单片机·串口
问心无愧051311 小时前
ctfshow web179,180
笔记
Persistent的粽子!12 小时前
C++的内存管理
c++·经验分享·笔记
kkkkkkkkkk_Z12 小时前
学嵌入式和Linux应用编程|学习日记:UDP协议与Wireshark抓包学习笔记
linux·笔记·学习
志尊宝12 小时前
Vue3 零基础每日笔记(018):按键与鼠标修饰符——回车搜索、Ctrl+S 保存这样写
javascript·vue.js·笔记