xyz*_*xyz 78 c# closures enumeration
在以下两个片段中,第一个是安全的还是你必须做第二个?
安全我的意思是每个线程保证从创建线程的相同循环迭代中调用Foo上的方法?
或者你必须将引用复制到一个新的变量"local"到循环的每次迭代?
var threads = new List<Thread>();
foreach (Foo f in ListOfFoo)
{
Thread thread = new Thread(() => f.DoSomething());
threads.Add(thread);
thread.Start();
}
Run Code Online (Sandbox Code Playgroud)
-
var threads = new List<Thread>();
foreach (Foo f in ListOfFoo)
{
Foo f2 = f;
Thread thread = new Thread(() => f2.DoSomething());
threads.Add(thread);
thread.Start();
}
Run Code Online (Sandbox Code Playgroud)
更新:正如Jon Skeet的回答所指出的,这与线程没有任何关系.
Mar*_*ell 102
编辑:这是C#5中的所有更改,更改了定义变量的位置(在编译器的眼中).从C#5开始,它们是相同的.
在C#5之前
第二是安全的; 第一个不是.
使用foreach
,变量在循环外声明- 即
Foo f;
while(iterator.MoveNext())
{
f = iterator.Current;
// do something with f
}
Run Code Online (Sandbox Code Playgroud)
这意味着f
在闭包范围方面只有1个,并且线程很可能会混淆 - 在某些实例上多次调用该方法而在其他实例上调用该方法.您可以使用循环内的第二个变量声明来解决此问题:
foreach(Foo f in ...) {
Foo tmp = f;
// do something with tmp
}
Run Code Online (Sandbox Code Playgroud)
然后tmp
,每个闭包范围都有一个单独的,因此不存在此问题的风险.
这是问题的简单证明:
static void Main()
{
int[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
foreach (int i in data)
{
new Thread(() => Console.WriteLine(i)).Start();
}
Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)
输出(随机):
1
3
4
4
5
7
7
8
9
9
Run Code Online (Sandbox Code Playgroud)
添加一个临时变量,它可以工作:
foreach (int i in data)
{
int j = i;
new Thread(() => Console.WriteLine(j)).Start();
}
Run Code Online (Sandbox Code Playgroud)
(每个号码一次,但当然订单不保证)
Jon*_*eet 37
Pop Catalin和Marc Gravell的答案是正确的.我想要添加的是我关于闭包的文章的链接(它讨论了Java和C#).只是觉得它可能会增加一些价值.
编辑:我认为值得给出一个没有线程不可预测性的例子.这是一个简短但完整的程序,显示了这两种方法."坏动作"列表打印10次; "好动作"列表从0到9计数.
using System;
using System.Collections.Generic;
class Test
{
static void Main()
{
List<Action> badActions = new List<Action>();
List<Action> goodActions = new List<Action>();
for (int i=0; i < 10; i++)
{
int copy = i;
badActions.Add(() => Console.WriteLine(i));
goodActions.Add(() => Console.WriteLine(copy));
}
Console.WriteLine("Bad actions:");
foreach (Action action in badActions)
{
action();
}
Console.WriteLine("Good actions:");
foreach (Action action in goodActions)
{
action();
}
}
}
Run Code Online (Sandbox Code Playgroud)
Pop*_*lin 17
您需要使用选项2,在更改变量周围创建闭包将在使用变量时使用变量的值,而不是在闭包创建时使用.
编辑:为了说清楚,在C#闭包中是" 词法闭包 ",这意味着它们不会捕获变量的值,而是捕获变量本身.这意味着当为变化的变量创建闭包时,闭包实际上是对变量的引用而不是它的值的副本.
Edit2:添加了所有博客帖子的链接,如果有人有兴趣阅读有关编译器内部的内容.
归档时间: |
|
查看次数: |
10662 次 |
最近记录: |