|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
简单的说:.net只有微软一家在做的,微软也不允许别人跟他做相同的工具,所以他就把需要的工具全部封装在.net的平台上了;而net网页编程是公开了。成绩:因为在多线程中利用了匿名函数外的部分变量而招致的Bug
实行代码
staticvoidMain(string[]args)
{
for(inti=0;i<10;i++)
{
Threadt=newThread(delegate()
{
Thread.Sleep(newRandom().Next(1,10000));
Console.Write(i+",");
});
t.Start();
}
}
将失掉输入:10,10,10,10,10,10,10,10,10,10,
而不是我们希冀的相似于:3,5,6,1,0,7,9,8,4,2,如许的输入。这是为何呢?(在实践项目中呈现这个Bug的代码请参考[1])
剖析:利用Reflector检察编译以后的代码
上面是编译前后的代码对照。(利用Reflector的详细办法请参考[2],这里仅展示了局)
<br>
能够发明,i关于匿名办法来讲算得上是“全局”变量,假如在线程处置i之前,i的值就被改动了的话,就会呈现我们不但愿呈现的了局。
办理办法1:利用更小局限的部分变量
我们做一个小小的变动,在for轮回内里界说一个变量j,让匿名函数只会见这个j
<br>
以是运转程序,可失掉准确的输入:3,5,6,1,0,7,9,8,4,2,
由此,我们能够做出一个假定:编译器会在声明匿名函数所利用的部分变量的中央声明AutoGenClass的实例。那末,能够推出另外一个结论:假如匿名函数内里利用了成员变量,那末ThreadStart对象也会酿成成员变量,有乐趣的话能够本人用Reflector看一下。
这个办法固然能无效办理成绩,但有一个弱点:声明一时变量的企图不分明。为了不哪天有个非常热情又不明就里的程序员以为“变量j基本和i一样嘛”而把j给移除,我激烈倡议在j的前面加一个正文:“这里声明一个一时变量j是有深意的,谁敢动它老子跟谁玩命!!”。为制止这个弱点,可思索利用上面的办理办法。
办理办法2:在匿名函数中只利用它的参数
<br>
此次匿名函数压根就没利用“全局”变量,以是一样可失掉准确的了局:9,7,0,5,3,8,1,6,4,2,
假如必要利用线程池,代码迥然不同:
staticvoidMain(string[]args)
{
for(inti=0;i<10;i++)
{
ThreadPool.QueueUserWorkItem(delegate(objectarg)
{
Thread.Sleep(newRandom().Next(1,1000));
Console.Write(arg+",");
},i);
}
Thread.Sleep(10000);
}
既然在匿名函数内里利用可(在函数外)被变动的部分变量是云云伤害,我们是不是应当往微软总部门前请愿,请求在匿名函数里只能利用被声明为readonly的部分变量呢?就这么定了!机票钱老赵出,如今报名……
偶还没YY够,Boss就喜洋洋的对我吼道,这TMD是怎样回事?为何你的程序输入的了局是无序的?即刻给我改成输入0,1,2,3,4,5,6,7,8,9,!
但是我们用的多线程呀,怎样能包管各个线程按按次获得数据呢?这类时分,能利用部分变量就便利多了。
办理办法3:利用泛型Queue传送数据
代码以下:
staticvoidMain(string[]args)
{
Queue<int>q=newQueue<int>();
for(inti=0;i<10;i++)
{
q.Enqueue(i);
Threadt=newThread(delegate()
{
Thread.Sleep(newRandom().Next(1,10000));
lock(q)
{
if(q.Count>0)
Console.WriteLine(Thread.CurrentThread.Name+":do"+q.Dequeue());
}
});
t.Name="线程"+i;
t.Start();
}
}输入:
线程2:do0
线程1:do1
线程7:do2
线程9:do3
线程3:do4
线程5:do5
线程6:do6
线程8:do7
线程0:do8
线程4:do9
请按恣意键持续...
利用了线程同步以后,线程们排着队往Queue里取数据,然后实行,在效力上就表现不出多线程的上风了。不外,假如换成用线程池使用背景余暇线程仍是成心义的。
伶俐的你必定想到了,泛型Queue一样能够经由过程参数传送出来。
staticvoidMain(string[]args)
{
Queue<int>q=newQueue<int>();
for(inti=0;i<10;i++)
{
q.Enqueue(i);
Threadt=newThread(delegate(objectarg)
{
Thread.Sleep(newRandom().Next(1,10000));
Queue<int>qq=argasQueue<int>;
lock(qq)
{
if(qq.Count>0)
Console.WriteLine(Thread.CurrentThread.Name+":do"+qq.Dequeue());
}
});
t.Name="线程"+i;
t.Start(q);
}
}
结论
既然.net供应了由线程向匿名函数传送参数值的功效,你想要定下一条“多线程回调的匿名函数只同意利用它的参数,克制利用函数外的变量”的礼貌是能够了解的。固然,因为参数只能有一个,以是必要传送多个数值的时分就必需提早机关一个数组或Struct,这几仍是有些方便。让我们往微软门前游行,请求为Thread.Start()供应一个可变参数的重载吧,机票钱老赵出……
参考文献
[1]JeffreyZhao,小心匿名办法酿成的变量共享。cnblogs,2009.
[2]菩提树下的杨过,使用Reflector把"闭包"看分明。cnblogs,2009.
[3]overred,《你不经常使用的c#之三》:Action之怪状。cnblogs,2009.
本文来自:http://www.ckuyun.com/1-2-3/archive/2009/03/16/thread-anonymous-method.html
以前学了大概半年时间的asp(没有机会做大系统,最多是自己对公司系统做些调整和修改还有一些小程序)。应该说开始接触asp.net是今年元月5号的事。现在很想把公司的系统重新用.net来架构,却不知道如何下手。 |
|