C# 基础之 No Thread.Abort No Die


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"); }); //线程取消之后输出
}

上面的代码就演示了一个安全退出线程的例子,工作线程会在结束前自行处理完后事,从而保证逻辑的可预测性。

文章索引

[隐 藏]

本站采用知识共享署名 3.0 中国大陆许可协议进行许可。 ©2014 Charley Box | 关于本站 | 浙ICP备13014059号