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();
}
...全文
853 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)
1、本课程是一个干货课程,主要讲解如何封装服务器底层,使用Tcp/ip长连接,IDE使用vs2019 c++开发以及使用c++11的一些标准,跨平台windows和linux,服务器性能高效,单服务器压力测试上万无压力,服务器框架是经历过上线产品的验证,框架简单明了,不熟悉底层封装的人,半个小时就能完全掌握服务器框架上手写业务逻辑。2、本课程是一个底层服务器框架教程,主要是教会学员在windows或linux下如何封装一个高效的,避免踩坑的商业级框架,服务器底层使用初始化即开辟内存的技术,使用内存池,服务器运行期间内存不会溢出,非常稳定,同时服务器使用自定义哈希hashContainer,在处理新的连接,新的数据,新的封包,以及解包,发包,粘包的过程,哈希容器性能非常高效,增、删、查、改永远不会随着连接人数的上升而降低性能,增、删、查、改的复杂度永远都是恒定的O(1)。3、服务器底层封装没有使用任何第三方网络库以及任何第三方插件,自由度非常的高,出了任何BUG,你都有办法去修改,查找问题也非常方便,在windows下使用iocp,linux下使用epoll.4、讲解c++纯客户端,主要用于服务器之间通信,也就是说你想搭建多层结构的服务器,服务器与服务器之间使用socket通信。还可以使用c++客户端做压力测试,开辟多线程连接服务器,教程提供了压力测试,学员可以自己做压力测试服务器性能。5、赠送ue4和unity3d通信底层框架以及多人交互demo,登录,注册,玩家离开,同步主要是教会学员服务器与客户端如何交互。6、赠送c++连接mysql数据库框架demo,登录,注册,玩家离开数据持久化.7、服务器教程使用自定义通信协议,同时也支持protobuf,选择权在开发者自己手里,想用什么协议都可以,自由度高。8、服务器教程使用手动敲代码逐句讲解的方式开展教学课程。非喜勿喷,谢谢大家。9、服务器教程提供源码,大家可以在平台提供的地址下载或者联系我,服务器使用c++11部分标准,std::thread,条件变量,线程锁,智能指针等,需要学员具备一定c++知识,购买前请慎重考虑。

110,534

社区成员

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

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

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