首页 > 编程语言 >C# 多线程访问之 SemaphoreSlim(信号量)【进阶篇】

C# 多线程访问之 SemaphoreSlim(信号量)【进阶篇】

时间:2023-03-22 11:35:48浏览次数:54  
标签:SemaphoreSlim 信号量 进阶篇 线程 semaphore Release 多线程 Wait

C# 多线程访问之 SemaphoreSlim(信号量)【进阶篇】

 

阅读目录


SemaphoreSlim 是对可同时访问某一共享资源或资源池的线程数加以限制的 Semaphore 的轻量替代,也可在等待时间预计很短的情况下用于在单个进程内等待。

由于 SemaphoreSlim 更加轻量、快速,因此推荐使用,本文也着重介绍。

回到顶部

一、简介

相较于线程锁的使一块代码只能一个线程访问,SemaphoreSlim 则是让同一块代码让多个线程同时访问,并且总数量可控。

SemaphoreSlim 尽可能多地依赖公共语言运行时 (CLR) 提供的同步基元。 还提供延迟初始化、基于内核的等待句柄。

SemaphoreSlim 也支持使用取消标记,但不支持命名信号量或使用用于同步的等待句柄。

线程通过调用从 WaitHandle 类中继承的 WaitOne 方法进入信号量,无论对于 System.Threading.Semaphore 对象、SemaphoreSlim.Wait 或 SemaphoreSlim.WaitAsync 方法还是 SemaphoreSlim 对象都适用。

当调用返回时,信号量计数会减少,当线程请求进入且计数为零时,此线程受到阻止。 线程通过调用 Semaphore.Release 或 SemaphoreSlim.Release 方法释放信号量时,允许受阻线程进入,此时信号量计数会增加。

受阻线程进入信号量无保证的顺序,比如先进先出 (FIFO) 或按后进先出 (LIFO)。

回到顶部

二、用法示例

关于 SemaphoreSlim、Wait()、Release() 的一个示例。

特别注意:若初始信号量为 0,则需要手动释放(Release())信号量。

  class Program
  {
  private static SemaphoreSlim semaphore;
  private static int padding; // 增加固定时间间隔,使输出更有序
  static void Main(string[] args)
  {
  // 创建 semaphore 对象
  semaphore = new SemaphoreSlim(0, 4); // (初始数量,最大数量)
  Console.WriteLine($"semaphore 中现有 {semaphore.CurrentCount} 个信号量");
  Task[] tasks = new Task[10];
  for (int i = 0; i <= 9; i++)
  {
  tasks[i] = Task.Run(() =>
  {
  Console.WriteLine($"任务 {Task.CurrentId} 准备进入 semaphore");
  int semaphoreCount;
  semaphore.Wait(); // 调用 Wait() 方法,标记等待进入信号量
  try
  {
  Interlocked.Add(ref padding, 100);
  Console.WriteLine($"任务 {Task.CurrentId} 进入 semaphore");
  Thread.Sleep(1000 + padding);
  }
  finally
  {
  semaphoreCount = semaphore.Release(); // 调用 Release() 方法,释放信号量
  // semaphoreCount:释放当前信号量之前的信号量
  }
  Console.WriteLine($"任务 {Task.CurrentId} 完成,释放 semaphore 一个信号量;释放前信号量:{semaphoreCount}");
  });
  }
  Thread.Sleep(500); // 暂时阻塞主线程,让全部线程到位
  Console.Write("主线程调用 Release(4) 释放四个信号量--> ");
  semaphore.Release(4); // 由于初始数量为 0 所以需要手动释放信号量
  Console.WriteLine($"semaphore 现有信号量 {semaphore.CurrentCount}");
  Task.WaitAll(tasks); // 等待全部线程完成
  Console.WriteLine("主线程退出");
  }
  }

 输出结果:

  

回到顶部

 三、属性 or 函数 or 方法释义

属性-AvailableWaitHandle

  返回一个 WaitHandle 对象,即封装等待对共享资源的独占访问的操作系统对象。

属性-CurrentCount

  指的是对于 SemaphoreSlim 对象,可以输入信号量的剩余线程数。

  属性的初始值 CurrentCount 由对类构造函数的 SemaphoreSlim 调用设置。 每次对 Wait 或 WaitAsync 方法的调用会递减,并按对 Release 方法的每此调用递增。

构造方法-SemaphoreSlim(Int32)

  public SemaphoreSlim (int initialCount);

  初始化 SemaphoreSlim 类的新实例,以指定可同时授予的请求的初始数量,但未指定最大请求数。

  若初始数量为 0,则需要手动释放指定数量的信号量;若小于 0,则抛出异常:ArgumentOutOfRangeException。

构造方法-SemaphoreSlim(Int32, Int32)

  public SemaphoreSlim (int initialCount, int maxCount);

  初始化 SemaphoreSlim 类的新实例,同时指定可同时授予的请求的初始数量和最大数量

  若initialCount 小于 0,或 initialCount 大于 maxCount,或 maxCount 小于等于 0,则抛出异常:ArgumentOutOfRangeException。

方法-Dispose

   用于释放由 SemaphoreSlim 类的当前实例使用的所有资源。

  SemaphoreSlim 与大多数成员不同,Dispose 不是线程安全的,不能与此实例的其他成员同时使用。

  // 若要释放托管资源和非托管资源,则为 true(缺省默认);
  // 若仅释放非托管资源,则为 false
  protected virtual void Dispose (bool disposing);

 方法-Release

  对 Release() 方法的调用将属性递增一个 CurrentCount 数。 如果在调用此方法之前 CurrentCount 属性值为零,该方法还允许调用 Wait 或 WaitAsync 方法,阻止一个线程或任务进入信号灯。

  // 释放 SemaphoreSlim 对象指定的次数
  public int Release (int releaseCount);

方法-Wait

  阻止当前线程,直至它可进入 SemaphoreSlim 对象为止。

  // 使用 TimeSpan 来指定超时,同时监视取消动作 CancellationToken
  [System.Runtime.Versioning.UnsupportedOSPlatform("browser")]
  public bool Wait (TimeSpan timeout, System.Threading.CancellationToken cancellationToken);
  // TimeSpan 表示要等待的毫秒数,-1 代表无限等待,0 代表立即返回-测试用例
  // 阻止当前线程,直至它可进入 SemaphoreSlim 为止
  // 并使用 32 位带符号整数来指定超时(-1 代表无限等待),同时监视取消操作 CancellationToken
  [System.Runtime.Versioning.UnsupportedOSPlatform("browser")]
  public bool Wait (int millisecondsTimeout, System.Threading.CancellationToken cancellationToken);
  // 返回值 bool:如果当前线程成功进入 SemaphoreSlim,则为 true;否则为 false
  // 阻止当前线程,直至它可进入 SemaphoreSlim 为止
  [System.Runtime.Versioning.UnsupportedOSPlatform("browser")]
  public void Wait ();

方法-WaitAsync

  此为 Wait 方法的异步方式,优势在于对线程不会独占,即不会独占当前线程直到释放信号量。

  若将本文第二部分中的代码:(两处修改)

  semaphore.Wait();
  // 1/2 改为以下,异步方式
  semaphore.WaitAsync();
   
  // 2/2 并记录线程 ID(在如下位置添加一行打印信息)
  try
  {
  Console.WriteLine($"任务 {Task.CurrentId} 进入 semaphore");
  Console.WriteLine($"ProcessorId {Thread.GetCurrentProcessorId()}");// 新增行
  Interlocked.Add(ref padding, 100);
  Thread.Sleep(1000 + padding);
  }

  输出的结果:(可见打印出来的线程 ID 有相同的情况,说明并非独占)

   

参考官方:SemaphoreSlim 类

注:个人记录,有问题欢迎指正。

本文来自博客园,作者:橙子家,微信号:zfy1070491745,有任何疑问欢迎沟通,一起成长。

转载本文请注明原文链接:https://www.cnblogs.com/czzj/p/16866137.html

标签:SemaphoreSlim,信号量,进阶篇,线程,semaphore,Release,多线程,Wait
From: https://www.cnblogs.com/sexintercourse/p/17243072.html

相关文章

  • 多线程 ForkJoinPool
    ava7提供了ForkJoinPool来支持将一个任务拆分成多个“小任务”并行计算,再把多个“小任务”的结果合并成总的计算结果。ForkJoinPool是ExecutorService的实现类,因此是一种......
  • 【Thread -- 1.1】 实现多线程的正确姿势
    【Thread--1.1】实现多线程的正确姿势一、实现多线程的方法有几种--两种1、正确方法--Oraclle官方文档--2种[Oraclle官方文档](Overview(JavaPlatformSE8)(ora......
  • fastapi多线程非阻塞启动
    1问题描述我在run.py文件下的主函数如下所示:importuvicornfromfastapiimportFastAPIapp=FastAPI(title="chatglm",description="开源版的chatglm接......
  • 【python】多线程并发,rpc接口性能测试
    1、官方文档https://docs.python.org/3/library/concurrent.futures.html 2、安装python3.x中自带了concurrent.futures模块python2.7需要安装futures模块,使用命令......
  • 玩转Serverless架构 概念篇+开发基础篇+开发进阶篇+场景案例篇
    你好,我是老蒋,一个在前端摸爬滚打7年的老兵,目前是国内某一线大厂的资深软件工程师。2017年国内Serverless技术刚刚兴起,我就开始使用和推广Serverless了,当时,我的团......
  • javaSE-day12(多线程)
    1.多线程的常用方法Thread提供了很多与线程操作相关的方法方法:publicvoidrun():线程的任务方法publicvoidstart():启动线程publicStringgetName():获取当前......
  • C++温故补缺(十五):多线程
    多线程参考:CSDN,知乎传统C++(C++11之前)中并没有引入线程的概念,如果想要在C++中实现多线程,需要借助操作系统平台提供的API,如Linux的<pthread.h>,或windows下的<windows.......
  • Java EasyExcel带格式多线程导出百万数据
     JavaEasyExcel带格式多线程导出百万数据1.背景说明2.方案概述(1)大数据量导出问题主要是以下三个地方:(2)将写入导出Excel等功能单独分开成一个微服务:(3)注意:(4)方......
  • Lniux有关python多线程的历史
    在内核2.6以前的调度实体都是进程,内核并没有真正支持线程。它是能过一个系统调用clone()来实现的,这个调用创建了一份调用进程的拷贝,跟fork()不同的是,这份进程拷贝完全共......
  • 多线程面试——CountDownLatch,CyclicBarrier,Semaphore
    0.总结1.CountDownLatch是1个线程等待其他线程,CyclicBarrier是多个线程相互等待;2.CountDownLatch是计数-1,直到减为0,CyclicBarrier是计数+1,直到达到指定值;3.CountDownLatch......