C#使用SemaphoreSlim实现并发控制与限流策略的实战指南
作者:拾荒的小海螺
在现代应用中(如爬虫、并发请求、数据库连接池、异步任务处理),我们常常需要限制同时执行的任务数量,以避免过载或资源竞争,在 C# 中,最简洁高效的解决方案之一就是SemaphoreSlim,本文就给大家介绍了C#使用SemaphoreSlim实现并发控制与限流策略的实战指南
1、简述
在现代应用中(如爬虫、并发请求、数据库连接池、异步任务处理),我们常常需要限制同时执行的任务数量,以避免过载或资源竞争。
在 C# 中,最简洁高效的解决方案之一就是 —— SemaphoreSlim。
2、基本原理
SemaphoreSlim 内部维护一个“许可计数(Permit Count)”。
每个线程执行前需要调用 Wait()(或 WaitAsync())来获取许可,执行结束后调用 Release() 归还许可。
当所有许可被占用时,新线程会等待,直到有资源释放。Semaphore 与 SemaphoreSlim 都用于控制并发访问,但它们的实现和性能不同。
| 对比项 | Semaphore | SemaphoreSlim |
|---|---|---|
| 实现方式 | 内核对象(较重) | 用户态轻量实现(高性能) |
| 是否跨进程 | ✅ 是 | ❌ 否 |
| 是否支持 async/await | ❌ 否 | ✅ 是 |
| 推荐场景 | 跨进程同步 | 应用内并发控制 |
结论: 在现代 C# 应用中(如 Web API、后台任务、异步 I/O),使用 SemaphoreSlim 是首选。
3、实践样例
3.1 基本用法
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static SemaphoreSlim semaphore = new SemaphoreSlim(3); // 最多3个并发
static async Task Main()
{
var tasks = new Task[10];
for (int i = 0; i < 10; i++)
{
int id = i;
tasks[i] = Task.Run(() => DoWorkAsync(id));
}
await Task.WhenAll(tasks);
Console.WriteLine("全部任务完成!");
}
static async Task DoWorkAsync(int id)
{
await semaphore.WaitAsync(); // 获取许可
try
{
Console.WriteLine($"任务 {id} 开始,当前时间:{DateTime.Now:T}");
await Task.Delay(1000); // 模拟工作
Console.WriteLine($"任务 {id} 结束。");
}
finally
{
semaphore.Release(); // 释放许可
}
}
}
输出示例:
任务 0 开始... 任务 1 开始... 任务 2 开始... 任务 3 等待中... 任务 0 结束。 任务 3 开始... ...
说明:同时最多 3 个任务在运行。
3.2 限制并发的 HTTP 请求(爬虫场景)
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
class WebCrawler
{
static readonly HttpClient httpClient = new HttpClient();
static readonly SemaphoreSlim semaphore = new SemaphoreSlim(5); // 最多5个并发
static async Task Main()
{
var urls = new[]
{
"https://example.com",
"https://dotnet.microsoft.com",
"https://github.com",
"https://openai.com",
"https://google.com",
"https://bing.com"
};
var tasks = new Task[urls.Length];
for (int i = 0; i < urls.Length; i++)
{
string url = urls[i];
tasks[i] = ProcessUrlAsync(url);
}
await Task.WhenAll(tasks);
Console.WriteLine("所有请求完成!");
}
static async Task ProcessUrlAsync(string url)
{
await semaphore.WaitAsync();
try
{
Console.WriteLine($"开始请求:{url}");
var response = await httpClient.GetAsync(url);
Console.WriteLine($"{url} 返回状态码:{response.StatusCode}");
}
finally
{
semaphore.Release();
}
}
}
效果:同一时间最多有 5 个请求在执行,防止网络或服务器过载。
3.3 控制文件写入任务
多个线程同时写同一个文件时,可以用 SemaphoreSlim 控制访问速率。
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
class FileWriter
{
static SemaphoreSlim semaphore = new SemaphoreSlim(1); // 一次仅允许1个线程写文件
static async Task Main()
{
var tasks = new Task[5];
for (int i = 0; i < 5; i++)
{
int id = i;
tasks[i] = Task.Run(() => WriteLogAsync(id));
}
await Task.WhenAll(tasks);
Console.WriteLine("写入完成。");
}
static async Task WriteLogAsync(int id)
{
await semaphore.WaitAsync();
try
{
await File.AppendAllTextAsync("output.txt", $"线程 {id} 在 {DateTime.Now}\n");
Console.WriteLine($"线程 {id} 写入完成");
}
finally
{
semaphore.Release();
}
}
}
结果:日志文件不会被多个线程同时写入而损坏。
3.4 带超时机制的并发控制
如果等待太久无法获取资源,可设置超时防止卡死。
if (await semaphore.WaitAsync(TimeSpan.FromSeconds(2)))
{
try
{
Console.WriteLine("成功获取资源。");
}
finally
{
semaphore.Release();
}
}
else
{
Console.WriteLine("等待超时,放弃操作。");
}
3.5 在异步队列中控制消费者数量
适用于后台任务或消息队列处理。
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
class QueueProcessor
{
static SemaphoreSlim semaphore = new SemaphoreSlim(3); // 同时处理3个任务
static BlockingCollection<int> queue = new BlockingCollection<int>();
static async Task Main()
{
// 模拟生产者
Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
queue.Add(i);
Console.WriteLine($"生产任务 {i}");
Thread.Sleep(200);
}
queue.CompleteAdding();
});
// 消费者
var consumers = new Task[3];
for (int i = 0; i < consumers.Length; i++)
{
consumers[i] = Task.Run(() => ConsumeAsync(i));
}
await Task.WhenAll(consumers);
Console.WriteLine("队列处理完成。");
}
static async Task ConsumeAsync(int workerId)
{
foreach (var item in queue.GetConsumingEnumerable())
{
await semaphore.WaitAsync();
try
{
Console.WriteLine($"工作线程 {workerId} 处理任务 {item}");
await Task.Delay(500);
}
finally
{
semaphore.Release();
}
}
}
}
效果:任务生产速度可以高于消费速度,但消费者数量始终受控,防止系统过载。
4、总结
通过本文,你已经掌握了:
SemaphoreSlim的核心原理与区别;- 如何控制并发访问;
- 在异步任务、HTTP 请求、文件 I/O 等场景下的实战用法;
- 如何结合超时与限流策略提高系统稳定性。
| 场景 | 说明 |
|---|---|
| 并发 HTTP 请求控制 | 限制同时发起的请求数 |
| 数据库连接池 | 限制连接数量,防止连接耗尽 |
| 图片处理任务 | 限制 CPU 密集型任务数 |
| 异步队列消费 | 避免后台任务过载 |
| API 限流 | 防止接口被滥用或打爆 |
SemaphoreSlim 是现代 .NET 并发编程中非常实用的工具, 能让你轻松实现资源保护、限流与异步协作。
以上就是C#使用SemaphoreSlim实现并发控制与限流策略的实战指南的详细内容,更多关于C# SemaphoreSlim并发控制与限流策略的资料请关注脚本之家其它相关文章!
