Thread 另类用法,如何执行一段可能死锁/卡死/死循环的代码

场景与需求

需要执行一段第三方的代码,这段代码可能死锁/卡死/死循环,在超时之后,如果没有结束,则认为任务执行失败,退出执行。

实现方案1:使用 Task 超时

实现方法参考:
https://www.cnblogs.com/jasongrass/p/10354727.html

但这里有一个问题,既然被执行的任务可能死锁,即可能永远不会结束(除非进程退出),如果使用上述方式,将有一个线程始终被占用,无法释放,这是很浪费资源的。

实现方案2:使用 Thread

基本思路:
执行任务,超时则将任务所在 Thread 终止(Abord)。

基本代码(还有需要完善,如支持返回值等。)

using System;
using System.Threading;
using System.Threading.Task;

    public class ForceCancellationAction
    {
        private CancellationTokenSource CancellationTokenSource { get; }

        private readonly Action _action;
        private readonly TimeSpan _timeout;

        public Exception BusinessException { get; private set; }

        public bool IsFinishedCauseTimeout { get; private set; }

        public ForceCancellationAction(Action action, TimeSpan timeout)
        {
            _action = action;
            _timeout = timeout;
            CancellationTokenSource = new CancellationTokenSource();
        }

        /// <summary>
        /// 执行指定的任务,如果任务执行超时,任务将被强制终止。
        /// </summary>
        /// <returns></returns>
        public Task Do()
        {
            Thread thread = new Thread(TargetThreadAction)
            {
                IsBackground = true
            };

            return Task.Run(async () =>
            {
                try
                {
                    thread.Start();
                    try
                    {
                        await Task.Delay(_timeout, CancellationTokenSource.Token);

                        // 计时到,强制终止目标线程
                        thread.Abort();
                    }
                    catch (TaskCanceledException)
                    {
                        // 目标线程中的工作正确结束
                    }
                }
                catch (Exception ex)
                {
                    Log.Error("[ForceCancellationAction]", ex);
                }

            });
        }

        private void TargetThreadAction()
        {
            try
            {
                _action();
            }
            catch (ThreadAbortException)
            {
                // 计时到,线程被强制终止。
                IsFinishedCauseTimeout = true;

                // ThreadAbortException 会被重新抛出,所以,需要调用 Thread.ResetAbort(); 重启线程。
                // [ThreadAbortException Class (System.Threading) | Microsoft Docs](https://docs.microsoft.com/zh-cn/dotnet/api/system.threading.threadabortexception?view=netframework-4.8 )
                Thread.ResetAbort();
            }
            catch (Exception ex)
            {
                Log.Error("[ForceCancellationAction]", ex);

                // 目标执行代码抛了业务异常
                BusinessException = ex;
            }
            finally
            {
                // 目标线程执行完毕,取消等待计时。
                CancellationTokenSource.Cancel();
            }
        }
    }

具体调用:

var forceAction = new ForceCancellationAction(() =>
{
    // 可能死锁/卡死/死循环的代码
}, TimeSpan.FromSeconds(2));
await forceAction.Do();

这样,在被执行任务出现意外卡死时,可以强制杀死线程。

注意点

需要注意的是,ThreadAbortException 捕获后,需要恢复线程,让其“自然”结束,因为这个异常是接不住的,会一直向上抛出,除非恢复线程。
参考:
ThreadAbortException Class (System.Threading) | Microsoft Docs

原文链接:
https://www.cnblogs.com/jasongrass/p/11252352.html

posted @ 2019-07-26 19:26 J.晒太阳的猫 阅读(...) 评论(...) 编辑 收藏