محدود کردن تعداد تسکهای غیر همزمان یک عملیات در C#
فرض کنید که قرار است برنامه ای بنویسیم و عملیاتی را به صورت چندین تسک غیر همزمان به صورت موازی انجام دهیم. برای مثال قرار است لیستی از صفحات وب را دانلود کرده و محتوای آن را پردازش کنیم. بدیهی است که عملیات دانلود و پردازش هر صفحه از صفحه دیگر جداست و می تواند به صورت موازی چندین آدرس را با هم دانلود و پردازش کنیم. اما نکته اینجاست که در صورت زیاد بودن تعداد آدرس ها، نمی توانیم همه آنها را یکجا دانلود و پردازش کنیم. برای همین می خواهیم مکانیزمی ایجاد کنیم که حداکثر N تسک به صورت همزمان اجرا شوند.
برای کنترل تعداد همزمانی درخواستها، میتوانیم از کلاس SemaphoreSlim
استفاده کنیم تا حداکثر تعداد تسک ها را محدود کنیم. هر زمان که یک درخواست می خواهد اجرا شود، اجازهی ورود جدید صادر میشود و پس از پایان نیز اجازه ورود برگردانده می شود. در اینجا یک نمونه کد برای انجام این کار آورده شده است:
public async Task MyOuterMethod()
{
var urls = new[] { "http://google.com", "http://yahoo.com", /* ... سایر URL ها ... */ };
var throttler = new SemaphoreSlim(initialCount: 20); // حداکثر 20 درخواست همزمان
var allTasks = new List<Task>();
foreach (var url in urls)
{
await throttler.WaitAsync(); // منتظر بمانید تا مجوز ارسال درخواست دریافت کنید
allTasks.Add(Task.Run(async () =>
{
try
{
using var client = new HttpClient();
var html = await client.GetStringAsync(url);
}
finally
{
throttler.Release(); // مجوز را آزاد کنید
}
}));
}
// منتظر بمانید تا همه درخواستها کامل شوند
await Task.WhenAll(allTasks);
}
در این روش، حداکثر 20 درخواست میتوانند به صورت همزمان اجرا شوند. هر بار که درخواست جدیدی قرار است شروع شود، اول تعداد تسکهای درحال اجرا چک می شوند اگر این تعداد کمتر از 20 باشد، اجازه ورود صادر می شود. با هر بار پایان یافتن یکی از درخواستها، تعداد تسک های درحال اجرا یکی کم می شود.
علاوه بر این، برای سناریوهای جدیدتر (مانند .NET 6)، میتوانید از Parallel.ForEachAsync
استفاده کنید که در آن میتوانید حداکثر تعداد همزمانی را با تنظیم گزینهی MaxDegreeOfParallelism
مشخص کنید. اما باید دقت کنید که استفاده از این API در برنامههای ASP.NET ممکن است مشکلساز باشد و باید از آن اجتناب کرد، زیرا این کار میتواند بر روی مقیاسپذیری برنامه تاثیر منفی بگذارد.
string[] urls = { "http://google.com", "http://yahoo.com", /*...*/ };
var client = new HttpClient();
var options = new ParallelOptions() { MaxDegreeOfParallelism = 20 };
await Parallel.ForEachAsync(urls, options, async (url, cancellationToken) =>
{
var html = await client.GetStringAsync(url, cancellationToken);
});