我正在编写一个使用ThreadPool.QueueUserWorkItem()
的Windows服务。每个线程都是一个短暂的任务。
当服务停止时,我需要确保当前正在执行的所有线程都完成。有没有办法等到队列自行清除?
答案 0 :(得分:8)
您可以在每个线程中创建一个事件(例如ManualResetEvent
),并将其保存在同步列表中(使用lock
构造)。任务完成后,设置事件或将其从列表中删除。
如果您想加入,可以使用WaitHandle.WaitAll
(MSDN documentation)等待所有事件发出信号。
这是一个黑客攻击,但我看不出如何将其简化为更简单!
编辑:此外,您可以确保没有发布新事件,然后等待几秒钟。如果它们确实是短命的,那么你就没有问题了。更简单,但更黑客。
最后,如果只是很短的时间,服务将不会退出,直到所有线程都死亡(除非它们是后台线程);因此,如果时间很短,服务控制经理就不会介意大约一秒钟 - 根据我的经验,您可以让它们过期 -
答案 1 :(得分:3)
执行此操作的标准模式是使用一个计数器来保存待处理工作项的数量,并使用一个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();