C#Semaphore&SemaphoreSlim

2023-11-03 15:49:36 浏览数 (3)

1. Semaphore

Semaphore 是一个.NET的线程同步对象,可以用来控制对资源的并行访问数量。Semaphore 在计算机科学中是一个很重要的概念,用于解决多线程编程中的各种问题。

基本上,Semaphore 是一个计数器,表示一个特定的资源可以被多少个线程同时访问。当一个线程试图进入一个受 Semaphore 控制的区块时,如果当前的计数大于零,则此线程可以继续执行,并且计数器会减一。如果计数器为零,则该线程将被阻塞,直到其他线程释放资源(计数器增加)。

在.NET中,你可以使用 System.Threading.Semaphore 类实现这个功能。

以下是一个简单的示例:

代码语言:javascript复制
// Creates a Semaphore object that can support up to three threads, with an initial count of zero threads.
Semaphore sem = new Semaphore(0, 3);

// Increment the semaphore count by three, allowing three threads to enter.
sem.Release(3);

// The current thread tries to enter the semaphore. If the count is greater than zero, it succeeds and the count is decremented by one.
sem.WaitOne();

Semaphore跨进程使用

使用 Semaphore 实现进程间同步的一种方式是使用具有特定名称的 Semaphore。在一个进程中创建命名的 Semaphore 后,可以在其他进程中通过名称打开并使用该 Semaphore

以下是创建和使用命名 Semaphore 的示例:

进程1:

代码语言:javascript复制
// 创建一个具有最大计数 3 和初始计数 0 的命名 Semaphore
Semaphore semaphore = new Semaphore(0, 3, "MySemaphore");

// 允许三个线程或进程访问资源
semaphore.Release(3);

// ... 进行其他工作 ...

// 不再需要 Semaphore 时关闭它
semaphore.Close();

进程2:

代码语言:javascript复制
// 在另一个进程中打开已经存在的命名 Semaphore
Semaphore existingSemaphore;
try
{
    existingSemaphore = Semaphore.OpenExisting("MySemaphore");
}
catch(System.Threading.WaitHandleCannotBeOpenedException)
{
    Console.WriteLine("The named semaphore does not exist.");
    return;
}

// 尝试进入 semaphore. 如果计数器大于零,则成功并将计数器减一
existingSemaphore.WaitOne();

// ... 进行工作 ...

// 完成工作后,释放访问权,以便其他等待的线程或进程可以进入
existingSemaphore.Release();

// ... 进行其他工作 ...

// 不再需要 Semaphore 时关闭它
existingSemaphore.Close();

这样便实现了两个或更多进程之间的同步。每个需要访问共享资源的进程都会调用 WaitOne() 方法。如果 Semaphore 的当前计数大于零,那么线程就可以继续执行并将 Semaphore的计数减一。如果计数为零,那么此线程将被阻塞,直到其他线程调用 Release() 方法增加计数。

2. SemaphoreSlim

SemaphoreSlim 是.NET 4.5引入的一个轻量级版本的 Semaphore,它主要用于在同一台机器上的任务和线程间进行同步,在性能上比 Semaphore 要好,但不能跨进程使用。

除了性能提升之外,SemaphoreSlim 还提供了异步支持,通过 WaitAsync 方法可以非阻塞地等待进入 Semaphore。

以下是一个简单的使用示例:

代码语言:javascript复制
// Creates a SemaphoreSlim object that can support up to three threads, with an initial count of zero threads.
SemaphoreSlim semSlim = new SemaphoreSlim(0, 3);

// Increment the semaphore count by three, allowing three tasks or threads to enter.
semSlim.Release(3);

// The current task or thread tries to enter the semaphore. 
// If the count is greater than zero, it succeeds and the count is decremented by one.
await semSlim.WaitAsync();

在使用完 SemaphoreSemaphoreSlim 后,应调用 Release 方法以便其它等待的线程或任务可以进入。

Task中使用SemaphoreSlim

在这个示例中,SemaphoreSlim 被用来限制在任意时刻只有一个 Task 可以进入临界区。其他尝试进入临界区的 Task 将被挂起,直到当前 Task 执行完毕并释放 SemaphoreSlim。

代码语言:javascript复制
SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);

List<Task> tasks = new List<Task>();

for (int i = 0; i < 10; i  )
{
    tasks.Add(Task.Run(async () =>
    {
        await semaphore.WaitAsync();

        try
        {
            // Critical section.
            Console.WriteLine("Task {0} in critical section.", Task.CurrentId);
            await Task.Delay(1000); // Simulate some work.
        }
        finally
        {
            Console.WriteLine("Task {0} leaving critical section.", Task.CurrentId);
            semaphore.Release();
        }
    }));
}

await Task.WhenAll(tasks);

Semaphore 和 SemaphoreSlim 区别

  • SemaphoreSlim 有更好的性能和内存效率,但只能在同一进程中使用。
  • Semaphore 可以跨进程使用,但性能和内存效率不如 SemaphoreSlim
  • SemaphoreSlim 支持异步操作。

0 人点赞