开门见山,下面的例子中通过调用ThreadPool.QueueUserWorkItem(WaitCallback callBack, object state)的方式实现异步调用:
1: class Program<!--CRLF-->
2: { <!--CRLF-->
3: static void Main(string[] args)<!--CRLF-->
4: { <!--CRLF-->
5: List<Action> actions = new List<Action>();<!--CRLF-->
6: actions.Add(() => Console.WriteLine("A1"));<!--CRLF-->
7: actions.Add(() => Console.WriteLine("A2"));<!--CRLF-->
8: actions.Add(() => Console.WriteLine("A3"));<!--CRLF-->
9: actions.Add(() => Console.WriteLine("A4"));<!--CRLF-->
10: <!--CRLF-->
11: foreach (var action in actions)<!--CRLF-->
12: { <!--CRLF-->
13: ThreadPool.QueueUserWorkItem(state => action(), null);<!--CRLF-->
14: } <!--CRLF-->
15: <!--CRLF-->
16: Console.Read(); <!--CRLF-->
17: } <!--CRLF-->
18: } <!--CRLF-->
但是出现错误的输出结果:
解决的方案就是在每次For循环中,调用Thread.Sleep休眠当前线程,哪怕是1ms:
1: class Program<!--CRLF-->
2: { <!--CRLF-->
3: static void Main(string[] args)<!--CRLF-->
4: { <!--CRLF-->
5: List<Action> actions = new List<Action>();<!--CRLF-->
6: actions.Add(() => Console.WriteLine("A1"));<!--CRLF-->
7: actions.Add(() => Console.WriteLine("A2"));<!--CRLF-->
8: actions.Add(() => Console.WriteLine("A3"));<!--CRLF-->
9: actions.Add(() => Console.WriteLine("A4"));<!--CRLF-->
10: <!--CRLF-->
11: foreach (var action in actions)<!--CRLF-->
12: { <!--CRLF-->
13: ThreadPool.QueueUserWorkItem(state => action(), null);<!--CRLF-->
14: <!--CRLF-->
15: Thread.Sleep(1); <!--CRLF-->
16: } <!--CRLF-->
17: <!--CRLF-->
18: Console.Read(); <!--CRLF-->
19: } <!--CRLF-->
20: } <!--CRLF-->
21: <!--CRLF-->
这次能够输出正确的结果:
我们也看到很多人确实是这么做的。但是如果真是必须这样的话,这样的编程方式很难让我接受,不知道大家有何高见。
在老赵的提示下,醒悟过来:由于被置于ThreadPool中的操作时异步的,还没有来的执行的时候,action已经被for循环改变,永远是同一个action对象! 呵呵,脑袋有时候有点转不过弯!
所以正确的写法是:
1: foreach (var action in actions)<!--CRLF-->
2: { <!--CRLF-->
3: var a = action; <!--CRLF-->
4: ThreadPool.QueueUserWorkItem(state => a(), null);<!--CRLF-->
5: } <!--CRLF-->
本文探讨了使用.NET ThreadPool的常见误区及正确实践。通过具体代码示例展示了如何避免因闭包捕获导致的问题,并提供了解决方案。强调了理解异步调用中变量作用域的重要性。
3万+

被折叠的 条评论
为什么被折叠?



