Task[] task = new Task[count];
            for (int i = 0; i < count; i++)
            {
                myModel my = new myModel();
                my.id = "ID:" + i.ToString();
                my.name = "姓名:" + i.ToString();
                my.age = "性别:" + i.ToString();                int a = i;
                task[i] = new Task(()=> {
                    list1.Add(i.ToString());  //   为什么这个I都是一样的
                    list1.Add(a.ToString()); //  这个就是正常的循环值
                    list_model.Add(my); 
                });
                task[i].Start();
            }

解决方案 »

  1.   

    task[i] = new Task(()=> {
                        Thread.Sleep(50);
                        list1.Add(i.ToString());  //  <span style="color: #FF00FF;"> 为什么这个I都是一样的</span>
                        list1.Add(a.ToString()); //  <span style="color: #FF0000;">这个就是正常的循环值</span>
                        list_model.Add(my); 
                    });线程中等一会再看看?
      

  2.   


    哇,神速。感谢。
    一样的,早试过了。还是有BUG。起初以为是 值类型和引用类型的问题,但是发现不是,很奇怪才特意过来发帖。
    你可以在本地运行下看看。
      

  3.   

    task[i].Start();
    task[i].Wait();
      

  4.   

    这是framework 4.0里面闭包的一个bug(也可以说是设计如此)。
    楼主再测一下framework 4.5之后的版本,应该结果是不一样的。
      

  5.   

    变量通过闭包传到另外的线程,情况会比较复杂,容易出现你这种与预期不符的情况。最好是不用闭包传值,将变量显式传递到新的线程中。Task 类型我没有使用过,不过我使用的线程启动方法,都会有一个带参数的重载的。
      

  6.   

    VS2015。
    委托
    匿名委托
    Lambda表达式。
    这个和委托没关系。i 赋值给 任何引用类型或值类型后都是正常的递增,为什么直接取 i 值 就是恒定不变的呢。
      

  7.   

    VS2015。
    委托
    匿名委托
    Lambda表达式。
    这个和委托没关系。i 赋值给 任何引用类型或值类型后都是正常的递增,为什么直接取 i 值 就是恒定不变的呢。这个和是否值类型无关。是因为你创建了新的线程,这样代码并不是按写的顺序执行的。
      

  8.   

    lambda表达式捕获的变量i指向同一块内存地址,
      

  9.   

    感谢,这个确实能解决问题,可惜是变成逐个执行了。稳妥起见,还是应该传递一个另外的新变量执行。7楼和8楼的前辈指出的闭包,应该是根本原因,变量作用域,一直都以为JS这样的脚本语言才有闭包,今天碰上了C#的闭包,再去学习下。
     
    多线程用的少,不好掌握,容易出现奇怪的BUG。
      

  10.   

    lambdal表达式不就是委托嘛。
      

  11.   

    这个跟.net的版本有关
      

  12.   

    i 一直都是count(尤其count值很小的时候) 这很正常。   
    new Task(()=> {
                        list1.Add(i.ToString());  //   为什么这个I都是一样的
                        list1.Add(a.ToString()); //  这个就是正常的循环值
                        list_model.Add(my); 
                    }); 你开启了多线程 在执行的时候 for 循环已经执行不知道到哪里了。 如果count 足够大的话i 就不一定是count 而是远大于a的一个值。
      

  13.   

     list1.Add(i.ToString());  //   为什么这个I都是一样的
                        list1.Add(a.ToString()); //  这个就是正常的循环值
                        list_model.Add(my);  
    在执行这三句话的时候 跟for循环中的i 在时间顺序上已经没有任何关系了,只是用了同一个变量而已。
      

  14.   

    不一样,调用lambdal表达式,在编译的时候编译器会创建一个匿名类,其构造函数的参数取决于从lambdal表达式外部传入的变量的个数。
      

  15.   


    编译器会把lambda表达式编译成一个匿名类,从lambdal表达式外部传入的变量,会成为类的字段,
    匿名类里面还有一个委托,
      

  16.   

    Task[] task = new Task[count];
                for (int i = 0; i < count; i++)
                {
                    myModel my = new myModel();
                    my.id = "ID:" + i.ToString();
                    my.name = "姓名:" + i.ToString();
                    my.age = "性别:" + i.ToString();
     
                    int a = i;
                    task[i] = new Task(()=> {
                        list1.Add(i.ToString());  //  <span style="color: #FF00FF;"> 为什么这个I都是一样的</span>
                        list1.Add(a.ToString()); //  <span style="color: #FF0000;">这个就是正常的循环值</span>
                        list_model.Add(my); 
                    });
                    task[i].Start();
                }==>
    for内部的这些语句,几乎是瞬间完成的,此时I值是count,也就是说每个task输出的I值,都是count
    而a不同,a暂存了I的值,后面,没有谁改变它的值
    这种代码很可怕,调用线程(一般是主线程)“瞬间”执行完,很可能开出的多个子线程才正在运行,这样我们知道I值是count,也算是可控的;
    但是,如果一个次线程提前执行完,其他次线程没有,输出结果就不都是count了,可能是count-1,或-2
      

  17.   

    次线程开出去后,成了脱缰的野马,顺序不可控,最终的执行由操作系统决定(当然windows操作系统也会参考各个线程的耗时)
      

  18.   

    线程不是立即启动的,实际启动时 for (int i = 0 循环已经结束了,所以看到的 i 都是循环终值
    你在循环中 int a = i; 创建 count 个临时变量,每个 a 都在生存周期内传递给了 TaskC# 继承了 C 的不良习惯:函数(过程)体内可不加声明的使用体外的变量,这容易把人绕晕
    如果显式的传递参数,或是声明某个变量是体外定义的,就将很清晰了