在看Artech的博客时发现他的这篇难道调用ThreadPool.QueueUserWorkItem()的时候,真是必须调用Thread.Sleep(N)吗? 讲到的一个匿名方法造成的问题,在文章后面,有老赵的回复,并且给出了解决方案(查看老赵的“警惕匿名方法造成的变量共享”)。其实不止匿名方法有这个困扰,我们在操作集合的时候,都应该全面考虑到”变量共享“问题。下面就贴一下自己加了几行注释的Artech的源码,从我自己的角度来分析一下:
class
Program
{
static void Main( string [] args)
{
List < Action > actions = new List < Action > ();
actions.Add(() => Console.WriteLine( " A1 " ));
actions.Add(() => Console.WriteLine( " A2 " ));
actions.Add(() => Console.WriteLine( " A3 " ));
actions.Add(() => Console.WriteLine( " A4 " ));
foreach (var action in actions)
{
// var tmpAction = action; // 线程执行这个委托方法就输出正常
// ThreadPool.QueueUserWorkItem(state => tmpAction(), null);
ThreadPool.QueueUserWorkItem(state => action(), null );
// Thread.Sleep(1); // 不管有没有这一行 都是有问题的
}
Console.Read();
}
}
{
static void Main( string [] args)
{
List < Action > actions = new List < Action > ();
actions.Add(() => Console.WriteLine( " A1 " ));
actions.Add(() => Console.WriteLine( " A2 " ));
actions.Add(() => Console.WriteLine( " A3 " ));
actions.Add(() => Console.WriteLine( " A4 " ));
foreach (var action in actions)
{
// var tmpAction = action; // 线程执行这个委托方法就输出正常
// ThreadPool.QueueUserWorkItem(state => tmpAction(), null);
ThreadPool.QueueUserWorkItem(state => action(), null );
// Thread.Sleep(1); // 不管有没有这一行 都是有问题的
}
Console.Read();
}
}
运行后,我们看到的结果和我们理想的相差甚远(加上Thread Sleep(1)那一行运行结果有时也不全是我们想要的结果)。
其实我们完全可以这样理解:在foreach循环的时候,action是一个委托方法引用,是引用类型,线程执行的时候,都将执行action变量所在的同一引用地址上的委托方法。而我们将action赋值给一个中间变量tmpAction后,每循环一次,就相当于在内存上重新分配了一段空间,然后线程执行一个新引用地址上的委托方法,这就避免了老赵所说的“匿名方法造成的变量共享”。
ps:我在早前一篇博客里讲到匿名方法的“一个需要注意的地方”的时候也提到了这一点,不知各位是否赞同。
本文转自JeffWong博客园博客,原文链接:http://www.cnblogs.com/jeffwongishandsome/archive/2009/12/05/1559998.html,如需转载请自行联系原作者