Мое приложение должно выполнять несколько задач для каждого арендатора каждую минуту. Это операции запуска и забывания, поэтому я не хочу использовать Parallel.ForEach для этого.
Вместо этого я перебираю список арендаторов и запускаю ThreadPool.QueueUserWorkItem для обработки каждой задачи арендаторов.
foreach (Tenant tenant in tenants)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessTenant), tenantAccount);
}
Этот код отлично работает на производстве и может обрабатывать более 100 клиентов менее чем за 5 секунд.
Однако при запуске приложения это приводит к 100% загрузке ЦП, в то время как такие вещи, как EF, нагреваются в процессе запуска. Чтобы ограничить это, я реализовал семафор следующим образом:
private static Semaphore _threadLimiter = new Semaphore(4, 4);
Идея состоит в том, чтобы ограничить обработку этой задачи только возможностью использовать только половину машинных логических процессоров. Внутри метода ProcessTenant я вызываю:
try
{
_threadLimiter.WaitOne();
// Perform all minute-to-minute tasks
}
finally
{
_threadLimiter.Release();
}
При тестировании это работает точно так, как ожидалось. Загрузка ЦП при запуске остается на уровне около 50% и, по-видимому, не влияет на скорость первоначального запуска.
Так что вопрос главным образом в том, что происходит на самом деле, когда вызывается WaitOne. Выпускает ли это поток для работы над другими задачами - аналогично асинхронным вызовам? В документации MSDN говорится, что WaitOne: «Блокирует текущий поток, пока текущий WaitHandle не получит сигнал.»
Так что я просто опасаюсь, что это не позволит моему веб-приложению продолжать использовать этот заблокированный поток во время его ожидания, что сделает весь смысл этого упражнения бессмысленным.