我有一个使用ThreadPool启动3..10个线程的场景。 每个线程都完成它的工作并返回ThreadPool。 所有后台线程完成后,主线程中可以通知哪些选项?
目前我正在使用一种自行开发的方法,为每个创建的线程递增一个变量,并在后台线程即将完成时递减它。 这很好用,但如果有更好的选择,我很好奇。
答案 0 :(得分:11)
除非使用Interlocked.Decrement
,否则递减变量(在线程之间)有点冒险,但如果你有最后一个线程(即当它变为零)时,该方法应该没有问题。请注意,它必须位于“finally”块中,以避免在异常情况下丢失它(此外,您不想终止该进程)。
在“Parallel Extensions”(或使用.NET 4.0)中,您可能还会查看此处的Parallel.ForEach
选项...这可能是将所有内容作为块完成的另一种方法。无需手动全部观看。
答案 1 :(得分:4)
试试这个:https://bitbucket.org/nevdelap/poolguard
using (var poolGuard = new PoolGuard())
{
for (int i = 0; i < ...
{
ThreadPool.QueueUserWorkItem(ChildThread, poolGuard);
}
// Do stuff.
poolGuard.WaitOne();
// Do stuff that required the child threads to have ended.
void ChildThread(object state)
{
var poolGuard = state as PoolGuard;
if (poolGuard.TryEnter())
{
try
{
// Do stuff.
}
finally
{
poolGuard.Exit();
}
}
}
可以以不同的方式使用多个PoolGuard来跟踪线程何时结束,并处理池已经关闭时尚未启动的线程。
答案 2 :(得分:3)
如果要等待的线程不超过64个,可以使用WaitHandle.WaitAll方法,如下所示:
List<WaitHandle> events = new List<WaitHandle>();
for (int i = 0; i < 64; i++)
{
ManualResetEvent mre = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(
delegate(object o)
{
Thread.Sleep(TimeSpan.FromMinutes(1));
((ManualResetEvent)o).Set();
},mre);
events.Add(mre);
}
WaitHandle.WaitAll(events.ToArray());
执行将等待,直到设置了所有ManualResetEvents,或者,您可以使用WaitAny方法。
WaitAny和WaitAll方法将阻止执行,但您可以简单地使用列表或链接到生成的任务的ManualResetEvents字典,以便稍后确定线程是否已完成。
答案 3 :(得分:2)
目前还没有一种内置方法可以做到这一点 - 我觉得这是使用池线程的最大麻烦之一。
正如Marc所说,这是在Parallel Extensions / .NET 4.0中修复的东西。
答案 4 :(得分:1)
你不能给每个线程一个不同的ManualResetEvent,并在完成后分别设置事件。然后,在主线程中,您可以等待传入的所有事件。
答案 5 :(得分:0)
如果您只想知道所有工作何时完成,并且不需要更精细的信息(就像您的情况一样),Marc的解决方案是最好的。
如果你想要一些线程来产生作业,而另一些线程要接收通知,你可以使用WaitHandle。代码要长得多。
int length = 10;
ManualResetEvent[] waits = new ManualResetEvent[length];
for ( int i = 0; i < length; i++ ) {
waits[i] = new ManualResetEvent( false );
ThreadPool.QueueUserWorkItem( (obj) => {
try {
} finally {
waits[i].Set();
}
} );
}
for ( int i = 0; i < length; i++ ) {
if ( !waits[i].WaitOne() )
break;
}
如上所述,WaitOne方法总是返回true,但是我已经这样写了,让你记住一些重载将Timeout作为参数。
答案 6 :(得分:0)
如何使用Semaphore,并为其设置一个与线程池一样多的限制。有一个获取信号量的方法,在你开始你的线程时被调用,当你的线程结束时释放它,如果你占用了所有的信号量,就会发出一个事件。