当我使用线程将内容打印到控制台时,为啥会产生奇怪的结果?
Posted
技术标签:
【中文标题】当我使用线程将内容打印到控制台时,为啥会产生奇怪的结果?【英文标题】:When I use threads to print things to the console, why does it produce odd results?当我使用线程将内容打印到控制台时,为什么会产生奇怪的结果? 【发布时间】:2013-05-07 08:43:39 【问题描述】:我最近在阅读 Rob Miles (here) 的非常好的 pdf 文件时进入了 Threads。他在第 160 页上有一个示例(2012,C# pdf),但它没有写入控制台,只是做了空循环。
我编写了一个非常简单的线程生成循环,它创建了 10 个线程,这些线程在 1000 的每个倍数上将它们的 ID 写入屏幕。这很好 - 它向我展示了线程是如何一起运行的。我的问题始于为什么我的输出如此混乱?通常当我运行下面的程序时,我会得到多个“线程 3 完成”行,我很确定,我应该只有一个。
我在循环中添加了来自 MSDN 的 "lock",但它似乎仍然会产生奇怪的输出(我将在下面举一个示例)。
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).
如果我能提供更多关于我尝试过的信息,请告诉我。
【问题讨论】:
我认为你在循环中的变量被捕获了。将 i 存储在临时变量中。但是,如果您的意思是为什么输出没有排序..线程的执行不能保证按照它开始的顺序。 不担心确切的顺序,但看起来@MatthewWatson 给出了相同的答案......谢谢。 C# Captured Variable In Loop的可能重复 【参考方案1】:尝试使用Parallel.For
,而不是自己在循环中启动线程:
Parallel.For(0, 10, DoThis);
如果您需要传递一个额外的参数(比如w
)作为其中的一部分,那么您可以这样做:
var w = 4;
Parallel.For(0, 10, i => DoThis(i, w));
当然要考虑的另一件事是Console
对象是独占的。如果你从一个线程中使用它,任何其他尝试使用它的线程都将被阻塞,直到它完成。
您的lock (sync)
也将阻止任何两个线程同时进行操作。
请记住,您的线程不能保证以任何特定顺序执行。
【讨论】:
我真的不介意什么顺序 - 锁是我试图找出问题所在!我从未使用过 Parallel.For(以及许多其他的东西),所以请快速阅读。 我还能将变量传递给 Dothis 方法吗?Parallel.For
将索引 (0..9) 作为参数传递给 Dothis
- Dothis
恰好与它所期望的委托类型相匹配,所以我给你的应该是工作”
对,你是......就在我让你“上钩”时,如果我的方法是 DoWork(int s, int w)...,你将如何修改它?
很酷,谢谢 - 现在我看到了,很简单。如果可以的话,我会再给你一个赞!【参考方案2】:
您的问题是您在循环变量上使用了“修改后的闭包”。
尽管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();
更多详情请看这里:
Access to Modified Closure
Eric Lippert's Article on Closures
【讨论】:
好吧,我正怀疑地看着那个“i”……让我快速玩一下。 @IV4for
循环的闭包没有改变,只有foreach
循环。见***.com/questions/16264289/…
@I4V 这与 .NET 4.5 和 C# 5 相同。循环 for (int i = 0; i < 10; i++) Thread myThread = new Thread(() => DoThis(i)); myThread.Start();
将变量 i
作为“外部”变量,就像它曾经是 int i; /* declared before loop */ for (i = 0; i < 10; i++) Thread myThread = new Thread(() => DoThis(i)); myThread.Start();
一样。跨度>
...但是,如果他使用foreach (var i in Enumerable.Range(0, 10)) Thread myThread = new Thread(() => DoThis(i)); myThread.Start();
,那将取决于他使用的 C# 版本。
谢谢大家! - 似乎我只需要知道要搜索什么!我可以看到线程是一个非常有趣的挑战。以上是关于当我使用线程将内容打印到控制台时,为啥会产生奇怪的结果?的主要内容,如果未能解决你的问题,请参考以下文章
当我从线程打印时,它使用 ncurses 在 C 中给了我奇怪的输出