为了账号安全,请及时绑定邮箱和手机立即绑定

foreach标识符和闭包

foreach标识符和闭包

富国沪深 2019-07-02 17:29:02
在下面的两个片段中,第一个是安全的,还是必须执行第二个?安全地说,我的意思是每个线程是否保证从创建线程的同一个循环迭代中调用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();}最新情况:正如JonSkeet的答案所指出的,这与线程无关。foreach标识符和闭包
查看完整描述

3 回答

?
慕桂英3389331

TA贡献2036条经验 获得超8个赞

这是C#5中的所有更改,并对定义变量的位置进行了更改(在编译器的眼里)。从…C#5以后,它们是相同的.


在C#5之前

第二个是安全的,第一个是不安全的。

带着foreach,变量被声明。循环-即

Foo f;while(iterator.MoveNext()){
     f = iterator.Current;
    // do something with f}

这意味着只有一个f就闭包范围而言,线程很可能会感到困惑-在某些实例上多次调用该方法,而对其他实例则完全不调用。您可以通过第二个变量声明来修复这个问题。循环:

foreach(Foo f in ...) {
    Foo tmp = f;
    // do something with 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();
        }

(每个数字一次,但订单当然不能保证)


查看完整回答
反对 回复 2019-07-02
?
胡说叔叔

TA贡献1804条经验 获得超8个赞

波普·卡特林和马克·格雷维尔的答案是正确的。我只想添加一个链接到我关于闭包的文章(讨论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();
        }
    }}


查看完整回答
反对 回复 2019-07-02
  • 3 回答
  • 0 关注
  • 835 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信