当我使用线程将东西打印到控制台时,为什么会产生奇怪的结果呢?

Sis*_*hus 8 c# multithreading thread-safety

我最近在阅读Rob Miles(这里)的非常好的pdf时进入了Threads .他在第160页(2012年,C#pdf)上有一个例子,但它没有写入控制台只是空循环.

我写了一个非常简单的线程生成循环,它创建了10个线程,在1000的每个倍数上将它们的ID写入屏幕.这很好 - 它向我展示了线程如何一起运行.我的问题从为什么我的输出如此困惑开始?通常当我运行下面的程序时,我会得到多个"Thread 3 Finished"行,我很确定,我应该只有一个.

我从MSDN 添加了一个"锁定"到循环但它似乎仍然产生奇数输出(我将在下面举一个例子).

    namespace ConsoleApplication1
    {
        class Program
        {
            static object sync = new object();

            static void Main(string[] args)
            {
                for (int i = 0; i < 10; i++)
                {
                    Thread myThread = new Thread(() => DoThis(i));
                    myThread.Start();
                }
                Console.ReadLine();
            }

            static void DoThis(int s)
            {
                lock (sync) // a new addition that hasn't helped
                {
                    for (long j = 0; j < 10000; j++)
                    {
                        if (j % 1000 == 0) Console.Write(String.Format("{0}.", s));
                    }
                    Console.WriteLine("\r\nThread {0} Finished", s);
                    Debug.Print("\r\nThread {0} Finished", s); //<-- added to debug

                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

我以为我做得很好 - 我有局部变量(我的计数循环),我有一个int传递大概不是通过引用,后来我试图锁定它做它的循环.没有快乐.我需要做些什么来使输出看起来合理?我试过Deubg.Print进行故障排除,但它也有错误(下面).

最终,我想在更大的应用程序中使用线程,但如果我不能在这里得到它,我不确定我想要!

示例最后从debug.print行输出:(注意倍数)...

Thread 1 Done
The thread '<No Name>' (0x15cc) has exited with code 0 (0x0).

Thread 9 Done
The thread '<No Name>' (0x1d0c) has exited with code 0 (0x0).

Thread 6 Done
The thread '<No Name>' (0x2248) has exited with code 0 (0x0).

Thread 10 Done
The thread '<No Name>' (0x22bc) has exited with code 0 (0x0).

Thread 9 Done
The thread '<No Name>' (0x85c) has exited with code 0 (0x0).

Thread 9 Done
The thread '<No Name>' (0x1628) has exited with code 0 (0x0).

Thread 3 Done
The thread '<No Name>' (0x2384) has exited with code 0 (0x0).

Thread 6 Done

Thread 2 Done

Thread 4 Done
The thread '<No Name>' (0x2348) has exited with code 0 (0x0).
The thread '<No Name>' (0x2420) has exited with code 0 (0x0).
The thread '<No Name>' (0x1ea8) has exited with code 0 (0x0).
Run Code Online (Sandbox Code Playgroud)

如果我能提供有关我尝试过的更多信息,请告诉我.

Mat*_*son 10

这里的问题是你在循环变量上使用了"修改后的闭包".

虽然这已针对foreach循环进行了修复,但for循环仍然存在问题(并且始终存在)

要解决此问题,请将Main()更改为:

static void Main(string[] args)
{
    for (int i = 0; i < 10; i++)
    {
        int copy = i; // Make a copy of i to prevent the "modified closure" problem.
        Thread myThread = new Thread(() => DoThis(copy));
        myThread.Start();
    }
    Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参见此处

访问Modified Closure

Eric Lippert关于闭包的文章

  • @ IV4对于`for`循环,闭包的东西没有改变,仅用于`foreach`循环.见http://stackoverflow.com/questions/16264289/captured-closure-loop-variable-in-c-sharp-5-0 (2认同)

Pho*_*cUK 5

尝试使用Parallel.For而不是在循环中自己启动线程:

Parallel.For(0, 10, DoThis);
Run Code Online (Sandbox Code Playgroud)

如果您需要传递一个额外的参数(比方说w)作为其中的一部分,那么您可以这样做:

var w = 4;
Parallel.For(0, 10, i => DoThis(i, w));
Run Code Online (Sandbox Code Playgroud)

当然要考虑的另一件事是该Console对象是独占的.如果您从一个线程使用它,那么尝试使用它的任何其他线程将被阻塞,直到它完成.

此外,您lock (sync)将阻止任何两个线程同时执行其操作.

请记住,不保证您的线程以任何特定顺序执行.