讲解C#中的信号量和屏障,以及它们在多线程编程中的应用场景
在 C# 中,信号量(Semaphore) 和 屏障(Barrier) 是多线程编程中常用的同步机制,它们用于解决不同的线程协调和同步问题。下面我将详细讲解它们的概念、用法以及实际应用场景。
1. 信号量(Semaphore)
1.1 概念
信号量 是一种用于控制 多个线程对共享资源的访问 的同步机制。它维护一个计数器,表示可以访问共享资源的线程数量:
- 当线程请求访问时,信号量的计数器减 1。
- 当线程释放资源时,信号量的计数器加 1。
信号量有两种类型:
- Semaphore:允许多个线程访问共享资源(计数信号量)。
- SemaphoreSlim:轻量级的信号量,适用于大多数情况下的并发编程。
1.2 使用 Semaphore 的示例
场景: 有 3 个资源,但 5 个线程要访问这些资源,每次最多只能允许 3 个线程访问资源。
using System;
using System.Threading;
class Program
{
// 初始化信号量,最多允许 3 个线程访问资源
static Semaphore semaphore = new Semaphore(3, 3);
static void Main()
{
for (int i = 1; i <= 5; i++)
{
Thread thread = new Thread(AccessResource);
thread.Name = #34;Thread {i}";
thread.Start();
}
}
static void AccessResource()
{
Console.WriteLine(#34;{Thread.CurrentThread.Name} is waiting to enter...");
// 请求信号量(等待资源)
semaphore.WaitOne();
Console.WriteLine(#34;{Thread.CurrentThread.Name} has entered the critical section.");
// 模拟线程占用资源的时间
Thread.Sleep(2000);
Console.WriteLine(#34;{Thread.CurrentThread.Name} is leaving the critical section.");
// 释放信号量
semaphore.Release();
}
}
输出示例:
Thread 1 is waiting to enter...
Thread 2 is waiting to enter...
Thread 3 is waiting to enter...
Thread 1 has entered the critical section.
Thread 2 has entered the critical section.
Thread 3 has entered the critical section.
Thread 4 is waiting to enter...
Thread 5 is waiting to enter...
Thread 1 is leaving the critical section.
Thread 4 has entered the critical section.
Thread 2 is leaving the critical section.
Thread 5 has entered the critical section.
1.3 信号量的应用场景
- 资源访问控制:限制多个线程对共享资源的同时访问数量。
- 连接池管理:如数据库连接池或网络连接池的管理。
- 限流控制:在高并发应用中限制同时执行的任务数量。
2. 屏障(Barrier)
2.1 概念
屏障(Barrier) 是一种用于 协调多个线程阶段性执行 的同步机制。所有线程必须在屏障点等待,直到所有线程都到达该点,才能继续执行下一阶段。
核心特点:
- 线程到达屏障点后会等待。
- 当所有参与线程都到达屏障点时,屏障解除,所有线程继续执行。
- 可以设置一个操作,在每次屏障点解除时执行(例如日志记录、进度更新等)。
2.2 使用 Barrier 的示例
场景: 4 个线程,每个线程需要分阶段完成任务,所有线程在每个阶段完成后,才能进入下一阶段。
using System;
using System.Threading;
class Program
{
static Barrier barrier = new Barrier(4, (b) =>
{
Console.WriteLine(#34;All threads reached the barrier. Phase {b.CurrentPhaseNumber} completed.\n");
});
static void Main()
{
for (int i = 1; i <= 4; i++)
{
Thread thread = new Thread(DoWork);
thread.Name = #34;Thread {i}";
thread.Start();
}
}
static void DoWork()
{
for (int phase = 0; phase < 3; phase++) // 模拟三阶段任务
{
Console.WriteLine(#34;{Thread.CurrentThread.Name} is working on Phase {phase}.");
Thread.Sleep(new Random().Next(1000, 2000)); // 模拟工作时间
// 到达屏障点并等待其他线程
barrier.SignalAndWait();
Console.WriteLine(#34;{Thread.CurrentThread.Name} passed the barrier for Phase {phase}.");
}
}
}
输出示例:
Thread 1 is working on Phase 0.
Thread 2 is working on Phase 0.
Thread 3 is working on Phase 0.
Thread 4 is working on Phase 0.
All threads reached the barrier. Phase 0 completed.
Thread 1 passed the barrier for Phase 0.
Thread 2 passed the barrier for Phase 0.
Thread 3 passed the barrier for Phase 0.
Thread 4 passed the barrier for Phase 0.
Thread 1 is working on Phase 1.
Thread 2 is working on Phase 1.
...
2.3 屏障的应用场景
- 多线程分阶段执行任务:需要确保每个阶段的任务都完成后,再进入下一阶段。
- 并行算法的同步:如分布式计算中,多个节点完成一部分计算后进行结果汇总。
- 阶段性同步:如多人协作项目,每个阶段的工作完成后再进入下一阶段。
3. 信号量与屏障的区别
特性 | 信号量(Semaphore) | 屏障(Barrier) |
作用 | 控制多个线程对共享资源的并发访问数量 | 使多个线程在某个阶段同步等待 |
控制机制 | 计数器(线程申请资源时计数减 1,释放时加 1) | 所有线程必须到达屏障点才能继续执行 |
线程等待时机 | 等待资源的可用性 | 等待所有线程到达屏障点 |
应用场景 | 资源访问控制、限流控制 | 分阶段任务、并行算法同步 |
4. 总结
- 信号量(Semaphore) 适用于控制多个线程对共享资源的访问,尤其在 资源池 和 限流控制 的场景下使用。
- 屏障(Barrier) 适用于需要 阶段性同步 的场景,保证所有线程在完成某一阶段任务后,再同步进入下一阶段。
通过合理选择信号量和屏障,可以有效地解决多线程编程中的线程同步问题,提高程序的执行效率和稳定性。