之前看了一些关于C#闭包的文章,里面大多都没有提到多线程的情形。
现在问题来了,如果我在闭包当中新开一个线程,让这个线程在主线程运行结束后运行,于是就会发生主线程中已经被销毁的局部变量能继续在新的线程中被使用的情形。
public class Test { public void foo() { int i = 5; Action a = () => { i = 3; }; new Thread(new ThreadStart( () => { Thread.Sleep(3000); a(); } )).Start(); while(true) { Console.WriteLine(i); Thread.Sleep(1000); if (i == 3) { Console.WriteLine(i); break; } } } }
static class Program { static void Main() { new Test().foo(); } }
从上面的代码中可以看到,当主线程结束的时候另一个线程中的委托a()还能访问到这个变量i,并修改它的值。
在这样简单的例子中可能看不出什么问题,但是如果这是一个很关键的数据呢?不是就会被破坏了吗?
这个问题该怎么解释,又该怎么解决呢?
我隐约记得你以前也问了个关于闭包捕获变量的问题,类似于如下:
for(int i=0;i<10;i++)
new Thread(net ThreadStart(()=>{Thread.Sleep(3000);Console.Write(i);})).Start();
刚好跟你现在提的这个问题疑问是相反的,也就是说你上次疑问是为什么输出不是0,1,2....9,而这次你却问,为什么输出是 0,1,2,....9.
那么,还是请看一下这篇文章吧:http://www.byywee.com/page/M0/S538/538163.html
不知道为什么老是提示“评论中有不当词汇”。只能贴成图片了,请见谅
还有,主要是因为C#没有规定被闭包捕获的变量要声明称const或readonly,因此就增加了不安全性。实践中只能说尽量避免这样操作吧
@飞鸟_Asuka: 那你应该把 C++ 也拉上:
// 捕获值,i 不可修改。
int i = 5;
auto f = [i](){int j = i + 5; };
// 捕获变量,i 可修改。
int i = 5;
auto f = [&i](){i++; };
这就是 C++ 复杂之处,也就是你必须明白你是想捕获变量,还是想捕获变量的值。
而 C# 就相对简单,只能捕获变量。
int i = 5; 不是分配在栈上。编译器创建了一个额外的类来容纳这个变量。深入理解c#有讲这个问题,可以看看