我的应用程序需要每分钟为每个租户执行多项任务。这些是即发即弃的操作,所以我不想使用 Parallel.ForEach 来处理这个问题。
相反,我正在遍历租户列表,并触发 ThreadPool.QueueUserWorkItem 来处理每个租户任务。
foreach (Tenant tenant in tenants)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessTenant), tenantAccount);
}
这段代码在生产环境中运行良好,通常可以在 5 秒内处理超过 100 个租户。
但是,在应用程序启动时,这会导致 100% 的 CPU 使用率,而 EF 之类的东西会在启动过程中预热。为了限制这一点,我实现了一个信号量,如下所示:
private static Semaphore _threadLimiter = new Semaphore(4, 4);
这个想法是将这个任务处理限制为只能使用一半的机器逻辑处理器。在我调用的 ProcessTenant 方法中:
try
{
_threadLimiter.WaitOne();
// Perform all minute-to-minute tasks
}
finally
{
_threadLimiter.Release();
}
在测试中,这似乎完全按预期工作。启动时的 CPU 利用率保持在 50% 左右,并且似乎不会影响初始启动的速度。
所以问题主要是关于调用 WaitOne 时实际发生的情况。这是否会释放线程以处理其他任务 - 类似于异步调用?MSDN 文档指出 WaitOne:“阻塞当前线程,直到当前 WaitHandle 接收到信号。”
所以我只是担心这实际上不会让我的网络应用程序在等待时继续使用这个被阻塞的线程,这将使这个练习的全部意义变得毫无意义。