C# C语言中的奇数(循环/线程/字符串/lambda)行为#
我有一段代码,因为闭包,我认为它可以工作;然而,结果证明并非如此。这里发生了什么事情使得它不能产生预期的输出(每个单词一个) 代码:C# C语言中的奇数(循环/线程/字符串/lambda)行为#,c#,multithreading,loops,closures,C#,Multithreading,Loops,Closures,我有一段代码,因为闭包,我认为它可以工作;然而,结果证明并非如此。这里发生了什么事情使得它不能产生预期的输出(每个单词一个) 代码: 这与闭包捕获变量本身的事实有关,在实际使用之前,闭包不会对变量进行求值。foreach循环结束后,text的值为“other”,循环结束后调用方法,调用时捕获变量text的值为“other” 详情请参阅。他解释了这种行为及其背后的一些原因。C#中的闭包在创建时没有捕获文本的价值。由于foreach循环在任何线程执行之前完成执行,因此将为每个线程指定text的最后一
这与闭包捕获变量本身的事实有关,在实际使用之前,闭包不会对变量进行求值。foreach循环结束后,
text
的值为“other”,循环结束后调用方法,调用时捕获变量text
的值为“other”
详情请参阅。他解释了这种行为及其背后的一些原因。C#中的闭包在创建时没有捕获文本的价值。由于foreach循环在任何线程执行之前完成执行,因此将为每个线程指定text
的最后一个值
这可以通过以下方式进行补救:
string[] source = new string[] {"this", "that", "other"};
List<Thread> testThreads = new List<Thread>();
foreach (string text in source)
{
// Capture the text before using it in a closure
string capturedText = text;
testThreads.Add(new Thread(() =>
{
Console.WriteLine(capturedText);
}));
}
testThreads.ForEach(t => t.Start());
string[]source=新字符串[]{“this”、“that”、“other”};
List testThreads=new List();
foreach(源中的字符串文本)
{
//在闭包中使用文本之前捕获文本
字符串capturedText=文本;
添加(新线程(()=>
{
Console.WriteLine(capturedText);
}));
}
testThreads.ForEach(t=>t.Start());
如您所见,此代码在for循环的每个迭代中“捕获”了
text
的值。这保证了闭包在每次迭代中都会得到一个唯一的引用,而不是在迭代结束时共享同一个引用。发生这种情况的原因是,当您开始线程时,循环已经完成,文本局部变量的值是“other”,因此当您开始线程时,将打印该值。这很容易解决:
string[] source = new string[] {"this", "that", "other"};
foreach (string text in source)
{
new Thread(t => Console.WriteLine(t)).Start(text);
}
其他人已经解释了为什么你会遇到这个问题 幸运的是,修复非常容易:
foreach (string text in source)
{
string textLocal = text; // this is all you need to add
testThreads.Add(new Thread(() =>
{
Console.WriteLine(textLocal); // well, and change this line
}));
}
这是捕获循环变量的典型错误。这对
for
和foreach
循环都有影响:假设一个典型的构造,在整个循环期间只有一个变量。当lambda表达式或匿名方法捕获变量时,捕获的是变量本身(而不是捕获时的值)。如果更改变量的值,然后执行委托,委托将“看到”该更改
Eric Lippert在他的博客中详细介绍了这一点:
通常的解决方案是在循环内复制变量:
string[]source=新字符串[]{“this”、“that”、“other”};
List testThreads=new List();
foreach(源中的字符串文本)
{
字符串副本=文本;
添加(新线程(()=>
{
控制台写入线(副本);
}));
}
testThreads.ForEach(t=>t.Start())
这样做的原因是每个委托现在将捕获
copy
变量的不同“实例”。捕获的变量将是为循环迭代创建的变量-为该迭代分配text
值。瞧,这一切都是可行的。闭包/lambda无法正确绑定到foreach或循环计数器变量。将该值复制到另一个局部变量(未声明为foreach或counter变量),它将按预期工作。我们又来了……这应该是一个编译器警告,就像在VB中一样。我认为C#团队不警告人们潜在的问题是愚蠢的。重复:@leppie,我认为这意味着我们永远找不到闭包,因为我们陷入了循环。可能的重复更具体地说,闭包只在调用时执行,而不是在声明时执行。在调用它时,Davy8所做的语句就是所发生的事情。
string[] source = new string[] {"this", "that", "other"};
foreach (string text in source)
{
new Thread(t => Console.WriteLine(t)).Start(text);
}
foreach (string text in source)
{
string textLocal = text; // this is all you need to add
testThreads.Add(new Thread(() =>
{
Console.WriteLine(textLocal); // well, and change this line
}));
}
string[] source = new string[] {"this", "that", "other"};
List<Thread> testThreads = new List<Thread>();
foreach (string text in source)
{
string copy = text;
testThreads.Add(new Thread(() =>
{
Console.WriteLine(copy);
}));
}
testThreads.ForEach(t => t.Start())