在下面的两个摘要中,第一个是安全的还是第二个必须做?
安全地说,每个线程是否保证从创建线程的同一循环迭代中调用Foo上的方法?
还是必须将引用复制到新变量“ local”到循环的每次迭代中?
var threads = new List<Thread>(); foreach (Foo f in ListOfFoo) { Thread thread = new Thread(() => f.DoSomething()); threads.Add(thread); thread.Start(); }
--
var threads = new List<Thread>(); foreach (Foo f in ListOfFoo) { Foo f2 = f; Thread thread = new Thread(() => f2.DoSomething()); threads.Add(thread); thread.Start(); }
更新: 正如Jon Skeet的答案中指出的那样,这与线程无关。
编辑:在C#5中,所有这些都发生了变化,更改了定义变量的位置(在编译器看来)。从 C#5开始,它们是相同的 。
在C#5之前
第二个是安全的;第一个不是。
使用foreach,变量在循环 外部 声明-即
foreach
Foo f; while(iterator.MoveNext()) { f = iterator.Current; // do something with f }
这意味着f就闭包范围而言只有1 ,并且线程很可能会感到困惑- 在某些实例上多次调用该方法,而在其他实例上根本不调用该方法。你可以用第二个变量声明,解决这个问题 的内部 循环:
f
foreach(Foo f in ...) { Foo tmp = f; // do something with tmp }
这样,tmp在每个关闭范围中都有一个单独的位置,因此没有发生此问题的风险。
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(); }
输出(随机):
1 3 4 4 5 7 7 8 9 9
添加一个临时变量,它可以工作:
foreach (int i in data) { int j = i; new Thread(() => Console.WriteLine(j)).Start(); }
(每个号码一次,但是当然不能保证顺序)