Ответ 1
SemaphoreSlim является очень хорошим решением в этом случае, и я настоятельно рекомендую OP, чтобы попробовать это, но ответ @Manoj имеет недостаток, как упомянуто в комментариях. Перед тем, как создать эту задачу, следует дождаться появления семафора.
Обновленный ответ: Как отметил @Vasyl, семафор может быть удален до завершения задач и вызовет исключение при вызове метода Release(), поэтому перед выходом из блока using необходимо дождаться завершения всех созданных задач.
int maxConcurrency=10;
var messages = new List<string>();
using(SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency))
{
List<Task> tasks = new List<Task>();
foreach(var msg in messages)
{
concurrencySemaphore.Wait();
var t = Task.Factory.StartNew(() =>
{
try
{
Process(msg);
}
finally
{
concurrencySemaphore.Release();
}
});
tasks.Add(t);
}
Task.WaitAll(tasks.ToArray());
}
Ответьте на Комментарии для тех, кто хочет увидеть, как семафор может быть расположен без Task.WaitAll
Запустите приведенный ниже код в консольном приложении, и это исключение будет Task.WaitAll
.
System.ObjectDisposedException: "Семафор удален".
static void Main(string[] args)
{
int maxConcurrency = 5;
List<string> messages = Enumerable.Range(1, 15).Select(e => e.ToString()).ToList();
using (SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency))
{
List<Task> tasks = new List<Task>();
foreach (var msg in messages)
{
concurrencySemaphore.Wait();
var t = Task.Factory.StartNew(() =>
{
try
{
Process(msg);
}
finally
{
concurrencySemaphore.Release();
}
});
tasks.Add(t);
}
// Task.WaitAll(tasks.ToArray());
}
Console.WriteLine("Exited using block");
Console.ReadKey();
}
private static void Process(string msg)
{
Thread.Sleep(2000);
Console.WriteLine(msg);
}
}