我一直在学习如何使用线程池,但我不确定池中的每个线程是否都正确执行,并且我怀疑有些线程被执行多次。我已将代码削减到最低限度,并一直使用 Debug.WriteLine 来尝试弄清楚发生了什么,但这会产生一些奇怪的结果。
我的代码如下(基于(不支持 STA 线程上多个句柄的 WaitAll https://stackoverflow.com/questions/4192834/waitall-for-multiple-handles-on-a-sta-thread-is-not-supported):
public void ThreadCheck()
{
string[] files;
classImport Import;
CountdownEvent done = new CountdownEvent(1);
ManualResetEvent[] doneEvents = new ManualResetEvent[10];
try
{
files = Directory.GetFiles(importDirectory, "*.ZIP");
for (int j = 0; j < doneEvents.Length; j++)
{
done.AddCount();
Import = new classImport(j, files[j], workingDirectory + @"\" + j.ToString(), doneEvents[j]);
ThreadPool.QueueUserWorkItem(
(state) =>
{
try
{
Import.ThreadPoolCallBack(state);
Debug.WriteLine("Thread " + j.ToString() + " started");
}
finally
{
done.Signal();
}
}, j);
}
done.Signal();
done.Wait();
}
catch (Exception ex)
{
Debug.WriteLine("Error in ThreadCheck():\n" + ex.ToString());
}
}
classImport.ThreadPoolCallBack 目前实际上没有执行任何操作。
如果我手动单步执行代码,我会得到:
线程 1 已启动
线程 2 已启动
....一路到....
线程 10 已启动
但是,如果我手动运行它,输出窗口将填充“线程 10 已启动”
我的问题是:我使用线程池的代码是否有问题,或者 Debug.WriteLine 的结果是否被多个线程混淆了?
问题是您正在使用循环变量(j
) 在 lambda 表达式中。
为什么这是一个问题的细节相当冗长 - 请参阅埃里克·利珀特的博客文章 http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx有关详细信息(另请阅读part 2 http://blogs.msdn.com/b/ericlippert/archive/2009/11/16/closing-over-the-loop-variable-part-two.aspx).
幸运的是,修复很简单:只需创建一个新的局部变量inside循环并在 lambda 表达式中使用它:
for (int j = 0; j < doneEvents.Length; j++)
{
int localCopyOfJ = j;
... use localCopyOfJ within the lambda ...
}
对于循环体的其余部分,只需使用即可j
- 只有当它被 lambda 表达式或匿名方法捕获时,它才会成为问题。
这是一个困扰很多人的常见问题 - C# 团队已经考虑更改foreach
循环(其中really看起来您已经在每次迭代中声明了一个单独的变量),但这会导致有趣的兼容性问题。 (例如,您可以编写运行良好的 C# 5 代码,而使用 C# 4 可能可以很好地编译,但实际上会被破坏。)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)