c# - 重新抛出任务中的异常不会使任务进入故障状态

标签 c# multithreading task thread-abort

考虑以下场景

 var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(2));
        var startNew = Task.Factory.StartNew(() =>
        {
            var currentThread = Thread.CurrentThread;
            try
            {
                using (cancellationTokenSource.Token.Register(currentThread.Abort))
                    new AutoResetEvent(false).WaitOne(Timeout.InfiniteTimeSpan);
            }
            catch (ThreadAbortException abortException)
            {
                throw new TimeoutException("Operation timeouted", abortException);
            }
        }, cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Current);
        startNew.ContinueWith(val => Console.WriteLine("Cancellation handled"), TaskContinuationOptions.OnlyOnCanceled);
        startNew.ContinueWith(val => Console.WriteLine("Fault handled"), TaskContinuationOptions.OnlyOnFaulted);
        startNew.ContinueWith(val => Console.WriteLine("Ran to completion handled"), TaskContinuationOptions.OnlyOnRanToCompletion);

抛开所有关于中止线程是邪恶的讨论,为什么这段代码没有使任务进入Faulted状态?但是删除 catch block 或调用

Thread.ResetAbort()

似乎可以达到目的

最佳答案

这是关于线程中止如何工作的:

try {
    try {
        try {
            Thread.CurrentThread.Abort();
        } catch(Exception e) {
            Console.WriteLine(e.GetType());
            throw new Exception();
        }
    } catch(Exception e) {
        Console.WriteLine(e.GetType());
    }
} catch(Exception e) {
    Console.WriteLine(e.GetType());
}

此代码打印:

System.Threading.ThreadAbortException
System.Exception
System.Threading.ThreadAbortException

因此,当处理您的自定义异常时,ThreadAbortException 将被重新抛出。

ThreadAbortException is a special exception that can be caught by application code, but is re-thrown at the end of the catch block unless ResetAbort is called. MSDN

现在让我们看看一些source :

/// <summary>
/// Executes the task. This method will only be called once, and handles bookeeping associated with
/// self-replicating tasks, in addition to performing necessary exception marshaling.
/// </summary>
private void Execute()
{
    if (IsSelfReplicatingRoot)
    {
        ExecuteSelfReplicating(this);
    }
    else
    {
        try
        {
            InnerInvoke();
        }
        catch (ThreadAbortException tae)
        {
            // Don't record the TAE or call FinishThreadAbortedTask for a child replica task --
            // it's already been done downstream.
            if (!IsChildReplica)
            {
                // Record this exception in the task's exception list
                HandleException(tae);

                // This is a ThreadAbortException and it will be rethrown from this catch clause, causing us to 
                // skip the regular Finish codepath. In order not to leave the task unfinished, we now call 
                // FinishThreadAbortedTask here.
                FinishThreadAbortedTask(true, true);
            }
        }
        catch (Exception exn)
        {
            // Record this exception in the task's exception list
            HandleException(exn);
        }
    }
}

如您所见,ThreadAbortException 情况有特殊的代码路径将任务转换为错误状态。当您通过 TimeoutException 隐藏 ThreadAbortException 时,不会采用该特殊代码路径。因此,当常规代码路径通过将异常记录在任务的异常列表中来处理异常时,ThreadAbortException 将被重新抛出,这会阻止任务正确转换到故障状态。

关于c# - 重新抛出任务中的异常不会使任务进入故障状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31574378/

相关文章:

java - Android-当我尝试在文本框中显示某些内容时崩溃

c# - 如何强制任务在 UI 线程上运行?

C# Backgroundwork 在抛出异常时不调用 e.Error

java - 这段Java代码中的 `Thread`是什么?

testing - 如何定义仅限于包的测试任务?

task - 如何在 CCS (Code Composer Studio) 上跟踪和分析 FreeRTOS 任务 9

c# - 如何在 Visual Studio 中从 C++/CLI 和 C# 调用 C++/CLI?

c# - 单击编辑按钮后从 GridView 获取单元格值第 2 轮

c# - 在数据库中插入 datagridview 的值

c# - 如何检查可空 bool 值是否为真?