C#使用SemaphoreSlim實現(xiàn)并發(fā)控制與限流策略的實戰(zhàn)指南
1、簡述
在現(xiàn)代應(yīng)用中(如爬蟲、并發(fā)請求、數(shù)據(jù)庫連接池、異步任務(wù)處理),我們常常需要限制同時執(zhí)行的任務(wù)數(shù)量,以避免過載或資源競爭。
在 C# 中,最簡潔高效的解決方案之一就是 —— SemaphoreSlim。
2、基本原理
SemaphoreSlim 內(nèi)部維護一個“許可計數(shù)(Permit Count)”。
每個線程執(zhí)行前需要調(diào)用 Wait()(或 WaitAsync())來獲取許可,執(zhí)行結(jié)束后調(diào)用 Release() 歸還許可。
當所有許可被占用時,新線程會等待,直到有資源釋放。Semaphore 與 SemaphoreSlim 都用于控制并發(fā)訪問,但它們的實現(xiàn)和性能不同。
| 對比項 | Semaphore | SemaphoreSlim |
|---|---|---|
| 實現(xiàn)方式 | 內(nèi)核對象(較重) | 用戶態(tài)輕量實現(xiàn)(高性能) |
| 是否跨進程 | ? 是 | ? 否 |
| 是否支持 async/await | ? 否 | ? 是 |
| 推薦場景 | 跨進程同步 | 應(yīng)用內(nèi)并發(fā)控制 |
結(jié)論: 在現(xiàn)代 C# 應(yīng)用中(如 Web API、后臺任務(wù)、異步 I/O),使用 SemaphoreSlim 是首選。
3、實踐樣例
3.1 基本用法
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static SemaphoreSlim semaphore = new SemaphoreSlim(3); // 最多3個并發(fā)
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("全部任務(wù)完成!");
}
static async Task DoWorkAsync(int id)
{
await semaphore.WaitAsync(); // 獲取許可
try
{
Console.WriteLine($"任務(wù) {id} 開始,當前時間:{DateTime.Now:T}");
await Task.Delay(1000); // 模擬工作
Console.WriteLine($"任務(wù) {id} 結(jié)束。");
}
finally
{
semaphore.Release(); // 釋放許可
}
}
}
輸出示例:
任務(wù) 0 開始... 任務(wù) 1 開始... 任務(wù) 2 開始... 任務(wù) 3 等待中... 任務(wù) 0 結(jié)束。 任務(wù) 3 開始... ...
說明:同時最多 3 個任務(wù)在運行。
3.2 限制并發(fā)的 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個并發(fā)
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} 返回狀態(tài)碼:{response.StatusCode}");
}
finally
{
semaphore.Release();
}
}
}
效果:同一時間最多有 5 個請求在執(zhí)行,防止網(wǎng)絡(luò)或服務(wù)器過載。
3.3 控制文件寫入任務(wù)
多個線程同時寫同一個文件時,可以用 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();
}
}
}
結(jié)果:日志文件不會被多個線程同時寫入而損壞。
3.4 帶超時機制的并發(fā)控制
如果等待太久無法獲取資源,可設(shè)置超時防止卡死。
if (await semaphore.WaitAsync(TimeSpan.FromSeconds(2)))
{
try
{
Console.WriteLine("成功獲取資源。");
}
finally
{
semaphore.Release();
}
}
else
{
Console.WriteLine("等待超時,放棄操作。");
}
3.5 在異步隊列中控制消費者數(shù)量
適用于后臺任務(wù)或消息隊列處理。
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
class QueueProcessor
{
static SemaphoreSlim semaphore = new SemaphoreSlim(3); // 同時處理3個任務(wù)
static BlockingCollection<int> queue = new BlockingCollection<int>();
static async Task Main()
{
// 模擬生產(chǎn)者
Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
queue.Add(i);
Console.WriteLine($"生產(chǎn)任務(wù) {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} 處理任務(wù) {item}");
await Task.Delay(500);
}
finally
{
semaphore.Release();
}
}
}
}
效果:任務(wù)生產(chǎn)速度可以高于消費速度,但消費者數(shù)量始終受控,防止系統(tǒng)過載。
4、總結(jié)
通過本文,你已經(jīng)掌握了:
SemaphoreSlim的核心原理與區(qū)別;- 如何控制并發(fā)訪問;
- 在異步任務(wù)、HTTP 請求、文件 I/O 等場景下的實戰(zhàn)用法;
- 如何結(jié)合超時與限流策略提高系統(tǒng)穩(wěn)定性。
| 場景 | 說明 |
|---|---|
| 并發(fā) HTTP 請求控制 | 限制同時發(fā)起的請求數(shù) |
| 數(shù)據(jù)庫連接池 | 限制連接數(shù)量,防止連接耗盡 |
| 圖片處理任務(wù) | 限制 CPU 密集型任務(wù)數(shù) |
| 異步隊列消費 | 避免后臺任務(wù)過載 |
| API 限流 | 防止接口被濫用或打爆 |
SemaphoreSlim 是現(xiàn)代 .NET 并發(fā)編程中非常實用的工具, 能讓你輕松實現(xiàn)資源保護、限流與異步協(xié)作。
以上就是C#使用SemaphoreSlim實現(xiàn)并發(fā)控制與限流策略的實戰(zhàn)指南的詳細內(nèi)容,更多關(guān)于C# SemaphoreSlim并發(fā)控制與限流策略的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#利用GDI+給圖片添加文字(文字自適應(yīng)矩形區(qū)域)
這篇文章主要給大家介紹了關(guān)于C#利用GDI+給圖片添加文字(文字自適應(yīng)矩形區(qū)域)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。2018-04-04

