Thread.Abort 简单粗暴,能不用就别用!
做过多线程编程的童鞋肯定不会对这个方法感到陌生,当需要终止线程的时候,往往会简单的调用一下Thread.Abort,然后线程就被乖乖终止了。这个方法看起来行之有效,而且又相当简单。那为什么又不建议使用呢?
Monitor.Enter(_lock); Debug.WriteLine("I am locked"); int i = 1; while (i<100) { Debug.WriteLine("Hi there"); Thread.Sleep(1000); i++; } Monitor.Exit(_lock);
Thread.Abort 终止线程的方式是在该线程上引发一个 ThreadAbortException 的异常,如果逻辑上没有对该异常进行捕获,则根据CLR的异常处理方式,该线程最终将被终止。
试想一下,如果在线程a循环的时候,另一个线程调用了 Thread.Abort 方法,那线程a就会抛出一个异常(ThreadAbortException)并终止,从而导致Monier.Exit没有被执行,那么就会产生死锁。
如果你说这种加锁方式OUT了,使用lock关键字代替就没问题了。那你就又想当然了,如果在代码中使用了非托管资源呢?
using(StreamReader sr = new StreamReader(...)) { // }
我们知道执行StreamReader构造函数的时候就会占用非托管资源,当还没来得及把这个新实例的地址赋值给sr的时候就出现了异常,那即使使用了using也没法释放托管资源。
更何况,我们在进行项目开发的时候,经常会使用第三方的代码库来执行一些操作,很难保证其它人的在代码编写中使用了安全的方式。如果使用Thread.Abort就可能会让后期的维护陷入非常纠结的地步,因为你无法预料强制终止一个线程可能会导致的异常逻辑。
使用 CancellationTokenSource
最安全的方式就是告诉该线程 “我希望你不要再执行了”,然后该线程自己把该处理的后事处理完后结束自己。我们可以自己写个信号量来处理,也可以使用FCL为什么我们提供的CancellationTokenSource。
CancellationTokenSource cts = new CancellationTokenSource(); private void Run() { Thread t = new Thread(() => { Monitor.Enter(_lock); Debug.WriteLine("I am locked"); int i = 1; while (i<100 && !cts.IsCancellationRequested) //判断是否已经被取消 { Debug.WriteLine("Hi there"); Thread.Sleep(1000); i++; } Monitor.Exit(_lock); }; t.Start(); } private void Stop() { cts.Cancel(); //设置为取消 cts.Token.Register(() => { Debug.WriteLine("thread is end"); }); //线程取消之后输出 }
上面的代码就演示了一个安全退出线程的例子,工作线程会在结束前自行处理完后事,从而保证逻辑的可预测性。