c# - 线程池 - 可能的线程执行顺序问题

标签 c# multithreading loops lambda threadpool

我一直在学习如何使用线程池,但我不确定池中的每个线程是否都正确执行,并且我怀疑有些线程被执行多次。我已将代码削减到最低限度,并一直在使用 Debug.WriteLine 来尝试弄清楚发生了什么,但这会产生一些奇怪的结果。

我的代码如下(基于( 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 的结果是否被多个线程混淆了?

最佳答案

问题在于您在 lambda 表达式中使用了循环变量 (j)。

为什么这是一个问题的细节相当冗长 - 请参阅 Eric Lippert's blog post了解详细信息(另请阅读 part 2 )。

幸运的是,解决方法很简单:只需在循环内创建一个新的局部变量,并在 lambda 表达式中使用它:

for (int j = 0; j < doneEvents.Length; j++)
{
    int localCopyOfJ = j;

    ... use localCopyOfJ within the lambda ...
}

对于循环体的其余部分,只使用 j 就可以了 - 只有当它被 lambda 表达式或匿名方法捕获时,它才会成为问题。

这是一个困扰很多人的常见问题 - C# 团队已考虑更改 foreach 循环的行为(其中确实看起来像您已经在每次迭代中声明了一个单独的变量),但这会导致有趣的兼容性问题。 (例如,您可以编写运行良好的 C# 5 代码,而使用 C# 4 它可能可以编译正常,但实际上会被破坏。)

关于c# - 线程池 - 可能的线程执行顺序问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4469554/

相关文章:

c# - Linux 上的 JetBrains Rider EAP : how to start service

c# - 在 C# 中优化尾调用

c# - 通用函数和新约束

c++ - 如何在多线程环境中使用旧的单线程 C++ 库

javascript - 添加到 JS 脚本中时,iMacros 脚本无法正确循环

c++ - 这种控制 while() 循环的做法是好的吗? C++

c# - 是否有更快的方法来突出显示正则表达式匹配项 (RichTextBox)

java - 信号量是如何工作的?

c# - 这个 C# await 是多余的吗?

Javascript 规模检查功能性能