【发布时间】:2023-03-06 03:49:01
【问题描述】:
我正在编写一个使用ThreadPool.QueueUserWorkItem() 的Windows 服务。每个线程都是一个短暂的任务。
当服务停止时,我需要确保当前正在执行的所有线程都完成。有什么方法可以等到队列自行清除?
【问题讨论】:
标签: multithreading .net-3.5 windows-services threadpool
我正在编写一个使用ThreadPool.QueueUserWorkItem() 的Windows 服务。每个线程都是一个短暂的任务。
当服务停止时,我需要确保当前正在执行的所有线程都完成。有什么方法可以等到队列自行清除?
【问题讨论】:
标签: multithreading .net-3.5 windows-services threadpool
您可以在每个线程中创建一个事件(例如ManualResetEvent),并将其保存在同步列表中(使用lock 构造)。任务完成后设置事件或将其从列表中删除。
当您想加入时,您可以使用WaitHandle.WaitAll (MSDN documentation) 等待所有事件发出信号。
这是一个 hack,但我不知道如何将其简化为更简单的方法!
编辑:另外,您可以确保没有发布新事件,然后等待几秒钟。如果它们确实是短暂的,那么您将没有问题。更简单,但更 hacky。
最后,如果只是很短的时间,服务不会退出,直到所有线程都死掉(除非它们是后台线程);所以如果时间很短,服务控制经理不会介意一秒钟左右 - 你可以让它们过期 - 根据我的经验。
【讨论】:
执行此操作的标准模式是使用一个计数器来保存待处理工作项的数量,以及一个ManualResetEvent,当计数器达到零时发出信号。这通常比为每个工作项使用WaitHandle 更好,因为当有很多同时工作项时,它不能很好地扩展。另外,一些静态的WaitHandle 方法无论如何只接受最多64 个实例。
// Initialize to 1 because we are going to treat the current thread as
// a work item as well. This is to avoid a race that could occur when
// one work item gets queued and completed before the next work item
// is queued.
int count = 1;
var finished = new ManualResetEvent(false);
try
{
while (...)
{
Interlocked.Increment(ref counter);
ThreadPool.QueueUserWorkItem(
delegate(object state)
{
try
{
// Your task goes here.
}
finally
{
// Decrement the counter to indicate the work item is done.
if (Interlocked.Decrement(ref count) == 0)
{
finished.Set();
}
}
});
}
}
finally
{
// Decrement the counter to indicate the queueing thread is done.
if (Interlocked.Decrement(ref count) == 0)
{
finished.Set();
}
}
finished.WaitOne();
【讨论】: