线程编程之-银行的接待窗口(使用SemaphoreSlim类)

  是传统 Semaphore 类的轻量级替代方案。以下是关于它的核心要点:

主要用途

  用于限制同时访问某个资源或资源池的线程数量。例如,限制同时只有 5 个线程可以访问数据库连接池或执行特定的 I/O 操作。

为什么叫“轻量级” (Lightweight)?

  • ‌用户态同步‌:SemaphoreSlim 主要在用户模式下运行,避免了频繁进入内核模式(Kernel Mode)带来的性能开销。
  • ‌自旋等待 (Spin-waiting)‌:在等待时间较短时,它会先进行短暂的自旋等待,而不是立即阻塞线程上下文切换,从而提高了高并发场景下的性能。
  • ‌内存占用更低‌:相比基于内核对象的 Semaphore,它的内存 footprint 更小。

与 Semaphore 的关键区别

 
特性SemaphoreSlimSemaphore
‌适用范围‌ 仅限‌单个进程‌内的线程同步 支持‌跨进程‌同步 (通过命名信号量)
‌性能‌ 更高 (适合短等待、高并发) 较低 (涉及内核调用)
‌异步支持‌ 支持 WaitAsync(),完美适配 async/await 不支持原生异步等待
‌命名支持‌ 不支持 支持 (可用于进程间通信)

 

  Semaphore:信号量。 相当于一个计数器,记录一个共享资源被访问线程的个数;好比有两个或者更多个办事窗口,他们都做同一件事;假设有4个窗口,五个窗口都没人访问的时候计数器为4;有一个窗口被占用计数器减1,为3;全部被占用则计数器为0;这时候其他人想要访问就必须等待占用结束后计数器加1;

 

 

 

 

 

 

 

  SemaphoreSlim属于混合模式,其允许我们在等待时间很短的情况下无需使用上下文切换。然而,有一个叫做Semaphore类的老版本。该版本使用纯粹的内核时间方式。一般没必要使用它,除非是非常重要的场景。我们可以创建一个具名的Semaphore,就像一个具名的Mutex一样,从而在不同的程序中同步线程。SemaphoreSlim并不使用Windows内核信号量,而且也不支持进程间同步。所以在跨程序同步的场景下可以使用Semaphore

 

下面的代码展示了,限制同时访问数据库线程为5个。

using System;
using System.Threading;
 
namespace semaphoreSlim_Test
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 1; i <= 6; i++)
            {
                string threadName = "Thread" + i;
                int secondsToWait = 2 + 2 * i;
                var t = new Thread(() => AccessDatabase(threadName, secondsToWait));
                t.Start();
            }
 
            Console.ReadKey();
        }
 
        static SemaphoreSlim _semaphor = new SemaphoreSlim(4);
 
        static void AccessDatabase(string name,int seconds)
        {
            Console.WriteLine("{0} waits to access a database", name);
            _semaphor.Wait();
            Console.WriteLine("{0} was granted an access to a database", name);
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            Console.WriteLine("{0} is completed", name);
            _semaphor.Release();
        }
    }
}

 

  当主程序启动时,创建了一个SemaphoreSlim的一个实例,并在其构造函数中指定允许的并发线程的数量,然后启动6个不同名称和不同初始运行时间的线程。

  每个线程都尝试获取数据库的访问,但我们借助于信号系统限制了访问数据库的并发线程最多为4个。所以当有4个线程获取了数据库的访问时,其余线程需等待,直到之前线程中的某一个完成工作并调用_semaphor.Release()方法来发出信号。

posted @ 2020-02-21 17:29  无聊的蚂蚁  阅读(260)  评论(0)    收藏  举报