for 循环的困惑,为什么i是恒定不变的

cyg17173 2017-12-28 02:30:11

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();
}
...全文
854 24 打赏 收藏 转发到动态 举报
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
cyg17173 2017-12-28
  • 打赏
  • 举报
回复
引用 21 楼 From_TaiWan 的回复:
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
引用 23 楼 xuzuning 的回复:
线程不是立即启动的,实际启动时 for (int i = 0 循环已经结束了,所以看到的 i 都是循环终值 你在循环中 int a = i; 创建 count 个临时变量,每个 a 都在生存周期内传递给了 Task C# 继承了 C 的不良习惯:函数(过程)体内可不加声明的使用体外的变量,这容易把人绕晕 如果显式的传递参数,或是声明某个变量是体外定义的,就将很清晰了
感谢各位前辈,要规避这些坑。
xuzuning 2017-12-28
  • 打赏
  • 举报
回复
线程不是立即启动的,实际启动时 for (int i = 0 循环已经结束了,所以看到的 i 都是循环终值 你在循环中 int a = i; 创建 count 个临时变量,每个 a 都在生存周期内传递给了 Task C# 继承了 C 的不良习惯:函数(过程)体内可不加声明的使用体外的变量,这容易把人绕晕 如果显式的传递参数,或是声明某个变量是体外定义的,就将很清晰了
秋的红果实 2017-12-28
  • 打赏
  • 举报
回复
次线程开出去后,成了脱缰的野马,顺序不可控,最终的执行由操作系统决定(当然windows操作系统也会参考各个线程的耗时)
秋的红果实 2017-12-28
  • 打赏
  • 举报
回复
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
sdfgrtyu 2017-12-28
  • 打赏
  • 举报
回复
引用 17 楼 zhuo_wp 的回复:
引用 13 楼 u010941149 的回复:
lambdal表达式不就是委托嘛。
不一样,调用lambdal表达式,在编译的时候编译器会创建一个匿名类,其构造函数的参数取决于从lambdal表达式外部传入的变量的个数。
编译器会把lambda表达式编译成一个匿名类,从lambdal表达式外部传入的变量,会成为类的字段, 匿名类里面还有一个委托,
zhuowp 2017-12-28
  • 打赏
  • 举报
回复
引用 18 楼 cyg17173 的回复:
[quote=引用 14 楼 zhuo_wp 的回复:] 这个跟.net的版本有关
前辈,看书真仔细,这个书是那一本啊,求 地址。[/quote]C#高级编程 第九版
cyg17173 2017-12-28
  • 打赏
  • 举报
回复
引用 14 楼 zhuo_wp 的回复:
这个跟.net的版本有关
前辈,看书真仔细,这个书是那一本啊,求 地址。
zhuowp 2017-12-28
  • 打赏
  • 举报
回复
引用 13 楼 u010941149 的回复:
lambdal表达式不就是委托嘛。
不一样,调用lambdal表达式,在编译的时候编译器会创建一个匿名类,其构造函数的参数取决于从lambdal表达式外部传入的变量的个数。
ourhouzi 2017-12-28
  • 打赏
  • 举报
回复
list1.Add(i.ToString()); // 为什么这个I都是一样的 list1.Add(a.ToString()); // 这个就是正常的循环值 list_model.Add(my); 在执行这三句话的时候 跟for循环中的i 在时间顺序上已经没有任何关系了,只是用了同一个变量而已。
zhuowp 2017-12-28
  • 打赏
  • 举报
回复
这个跟.net的版本有关
ourhouzi 2017-12-28
  • 打赏
  • 举报
回复
引用 楼主 cyg17173 的回复:

 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();
            }
i 一直都是count(尤其count值很小的时候) 这很正常。 new Task(()=> { list1.Add(i.ToString()); // 为什么这个I都是一样的 list1.Add(a.ToString()); // 这个就是正常的循环值 list_model.Add(my); }); 你开启了多线程 在执行的时候 for 循环已经执行不知道到哪里了。 如果count 足够大的话i 就不一定是count 而是远大于a的一个值。
sdfgrtyu 2017-12-28
  • 打赏
  • 举报
回复
lambdal表达式不就是委托嘛。
sdfgrtyu 2017-12-28
  • 打赏
  • 举报
回复
lambda表达式捕获的变量i指向同一块内存地址,
cyg17173 2017-12-28
  • 打赏
  • 举报
回复
引用 6 楼 duanzi_peng 的回复:
task[i].Start(); task[i].Wait();
感谢,这个确实能解决问题,可惜是变成逐个执行了。稳妥起见,还是应该传递一个另外的新变量执行。
引用 8 楼 closurer 的回复:
变量通过闭包传到另外的线程,情况会比较复杂,容易出现你这种与预期不符的情况。 最好是不用闭包传值,将变量显式传递到新的线程中。 Task 类型我没有使用过,不过我使用的线程启动方法,都会有一个带参数的重载的。
引用 7 楼 wddw1986 的回复:
这是framework 4.0里面闭包的一个bug(也可以说是设计如此)。 楼主再测一下framework 4.5之后的版本,应该结果是不一样的。
7楼和8楼的前辈指出的闭包,应该是根本原因,变量作用域,一直都以为JS这样的脚本语言才有闭包,今天碰上了C#的闭包,再去学习下。 多线程用的少,不好掌握,容易出现奇怪的BUG。
闭包客 2017-12-28
  • 打赏
  • 举报
回复
引用 9 楼 cyg17173 的回复:
[quote=引用 4 楼 u010941149 的回复:] 你用的是委托,你懂委托的原理吗? 你用的是vs2010?
VS2015。 委托 匿名委托 Lambda表达式。 这个和委托没关系。i 赋值给 任何引用类型或值类型后都是正常的递增,为什么直接取 i 值 就是恒定不变的呢。[/quote] 这个和是否值类型无关。是因为你创建了新的线程,这样代码并不是按写的顺序执行的。
闭包客 2017-12-28
  • 打赏
  • 举报
回复
变量通过闭包传到另外的线程,情况会比较复杂,容易出现你这种与预期不符的情况。 最好是不用闭包传值,将变量显式传递到新的线程中。 Task 类型我没有使用过,不过我使用的线程启动方法,都会有一个带参数的重载的。
exception92 2017-12-28
  • 打赏
  • 举报
回复
task[i].Start(); task[i].Wait();
cyg17173 2017-12-28
  • 打赏
  • 举报
回复
引用 4 楼 u010941149 的回复:
你用的是委托,你懂委托的原理吗? 你用的是vs2010?
VS2015。 委托 匿名委托 Lambda表达式。 这个和委托没关系。i 赋值给 任何引用类型或值类型后都是正常的递增,为什么直接取 i 值 就是恒定不变的呢。
sdfgrtyu 2017-12-28
  • 打赏
  • 举报
回复
弄错,,,,,,,,,,
cheng2005 2017-12-28
  • 打赏
  • 举报
回复
这是framework 4.0里面闭包的一个bug(也可以说是设计如此)。 楼主再测一下framework 4.5之后的版本,应该结果是不一样的。
加载更多回复(4)

110,545

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧