8

我最近在阅读 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

                }
            }
        }
    }

我以为我做得很好 - 我有局部变量(我的计数循环),我有一个可能不是通过引用传递的 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).

让我知道我是否可以提供有关我尝试过的更多信息。

4

2 回答 2

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();
}

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

访问修改后的闭包

Eric Lippert 关于闭包的文章

于 2013-05-13T10:39:00.270 回答
5

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

Parallel.For(0, 10, DoThis);

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

var w = 4;
Parallel.For(0, 10, i => DoThis(i, w));

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

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

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

于 2013-05-13T10:38:51.383 回答