SemaphoreSlim多线程同步

10

title: SemaphoreSlim
description:
published: true
date: 2024-11-09T05:40:09.467Z
tags:
editor: markdown

dateCreated: 2024-11-09T05:40:09.467Z

SemaphoreSlim 是对可同时访问某一共享资源,或资源池的线程数加以限制的 Semaphore 的轻量替代,相较于线程锁的使一块代码只能一个线程访问,SemaphoreSlim 则是让同一块代码让多个线程同时访问,并且总数量可控。

创建 SemaphoreSlim 实例

// 创建一个初始计数为2的 SemaphoreSlim 实例,最大计数为4
SemaphoreSlim semaphore = new SemaphoreSlim(initialCount: 2, maxCount: 4);

等待信号

// 同步等待信号
semaphore.Wait();
// 异步等待信号
await semaphore.WaitAsync();

释放信号

// 释放一个信号
semaphore.Release();
// 释放两个信号
semaphore.Release(2);

示例:控制并发任务数

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    private static SemaphoreSlim semaphore = new SemaphoreSlim(1, 1); 

    static async Task Main(string[] args)
    {
        var tasks = new Task[5];
        for (int i = 0; i < tasks.Length; i++)
        {
            var t = AccessResourceAsync(i);  
            tasks[i] = t;
        }
        await Task.WhenAll(tasks);
    }

    private static async Task AccessResourceAsync(int id)
    {
        await semaphore.WaitAsync();
        try
        {
            Console.WriteLine($"Task {id} is accessing the resource.");
            await Task.Delay(1000); // 模拟对资源的访问
            Console.WriteLine($"Task {id} has finished accessing the resource.");
        }
        finally
        {
            semaphore.Release();
        }
    }
}

Task.WhenAll(tasks) 中的 task 是同时执行,执行顺序不一定。输出结果:

Task 0 is accessing the resource.  
Task 0 has finished accessing the resource.  
Task 1 is accessing the resource.  
Task 1 has finished accessing the resource.  
Task 2 is accessing the resource.  
Task 2 has finished accessing the resource.  
Task 4 is accessing the resource.  
Task 4 has finished accessing the resource.  
Task 3 is accessing the resource.  
Task 3 has finished accessing the resource.

改成最多允许两个线程同时访问:

private static SemaphoreSlim semaphore = new SemaphoreSlim(1, 1); 

输出:

Task 0 is accessing the resource.  
Task 2 is accessing the resource.  
Task 0 has finished accessing the resource.  
Task 2 has finished accessing the resource.  
Task 1 is accessing the resource.  
Task 3 is accessing the resource.  
Task 3 has finished accessing the resource.  
Task 1 has finished accessing the resource.  
Task 4 is accessing the resource.  
Task 4 has finished accessing the resource.

若初始信号量为 0,则需要手动释放(Release())信号量。Release(Int32) 可释放多个信号。

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