调用Wait()或WaitAll()时,任务总是取消

本文关键字:任务 取消 WaitAll Wait 调用 | 更新日期: 2023-09-27 18:24:43

我有一个Task.Factory.StartNew的包装器,名为TaskManager,我需要实现它,以便在新线程中添加异常处理。这是我的课:

public static class TaskManager
{
    public static Task StartNew(Action action, IUserContextBase userContext, Action<Task> onCompletedCallback = null)
    {
        return AddContinueWithToTask(Task.Factory.StartNew(action), userContext, null, onCompletedCallback);
    }
    public static Task StartNew(Action action, IUserContextBase userContext, CancellationToken cancellationToken, TaskCreationOptions taskCreationOptions, TaskScheduler taskScheduler, Action<Task> onCompletedCallback = null)
    {
        return AddContinueWithToTask(Task.Factory.StartNew(action, cancellationToken, taskCreationOptions, taskScheduler), userContext, null, onCompletedCallback);
    }
    public static Task StartNew(Action action, IUserContextBase userContext, TaskCreationOptions taskCreationOptions, Action<Task> onCompletedCallback = null)
    {
        return AddContinueWithToTask(Task.Factory.StartNew(action, taskCreationOptions), userContext, null, onCompletedCallback);
    }
    public static Task StartNew(Action<object> action, IUserContextBase userContext, object state, Action<Task> onCompletedCallback = null)
    {
        return AddContinueWithToTask(Task.Factory.StartNew(action, state), userContext, null, onCompletedCallback);
    }
    public static Task StartNew(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions taskCreationOptions, TaskScheduler taskScheduler, Action<Task> onCompletedCallback = null)
    {
        return AddContinueWithToTask(Task.Factory.StartNew(action, null, cancellationToken, taskCreationOptions, taskScheduler), null, null, onCompletedCallback);
    }
    public static Task StartNew(Action<object> action, object state, Action<System.Exception> onExceptionCallback = null, Action<Task> onCompletedCallback = null)
    {
        return AddContinueWithToTask(Task.Factory.StartNew(action, state), null, onExceptionCallback, onCompletedCallback);
    }
    public static Task StartNew(Action action, Action<System.Exception> onExceptionCallback = null)
    {
        return AddContinueWithToTask(Task.Factory.StartNew(action), null, onExceptionCallback);
    }
    public static Task<TResult> StartNew<TResult>(Func<TResult> function, IUserContextBase userContext, CancellationToken cancellationToken)
    {
        return Task.Factory.StartNew(function, cancellationToken).ContinueWith(t =>
        {
            ManageException(t, userContext);
            return t.Result;
        }, TaskContinuationOptions.OnlyOnFaulted);
    }
    private static void ManageException(Task t, IUserContextBase userContext, Action<System.Exception> onExceptionCallback = null)
    {
        if (t.Exception != null)
            if (onExceptionCallback != null)
                onExceptionCallback(t.Exception);
            else
                t.Exception.Handle(ex =>
                {
                    GlobalContainer.Unity.Resolve<IDiagnosticLogHandler>().LogError(userContext, ex, ErrorSeverity.Normal);
                    return true;
                });          
    }
    private static Task AddContinueWithToTask(Task task, IUserContextBase userContext, Action<System.Exception> onExceptionCallback = null, Action<Task> onCompletedCallback = null)
    {
        return task
            .ContinueWith(t => ManageException(t, userContext, onExceptionCallback), TaskContinuationOptions.OnlyOnFaulted)
            .ContinueWith(t2 => { if (onCompletedCallback != null) onCompletedCallback(t2); })
            .ContinueWith(t => ManageException(t, userContext, onExceptionCallback), TaskContinuationOptions.OnlyOnFaulted);
    }

然而,每当有人使用我的类时(它返回一个Task),然后调用Task。等待()或任务。WaitAll()之后,他们似乎得到了一个异常,表示任务(全部)已被取消。错误发生在Wait()或WaitAll()处。我的TaskManager类可能出了什么问题?

调用Wait()或WaitAll()时,任务总是取消

实际上是将原始任务的延续返回给调用者,而不是实际任务。

您的延续有TaskContinuationOptions.OnlyOnFaulted,这意味着只有在先行件出现故障时才运行它,否则取消它。因此,您可以看到调用者得到TaskCancelledException

换句话说,当实际任务运行到完成时,Continuation将被取消。当实际任务出现故障时,Continuation将运行到完成,前提是使用了TaskContinuationOptions.OnlyOnFaulted

要修复它,您需要将实际任务返回给调用者,但添加延续并将其保留。

更新:要返回原始任务,只需使用一个局部变量。

public static Task StartNew(Action action, Action<System.Exception> onExceptionCallback = null)
{
    var actualTask = Task.Factory.StartNew(action);
    AddContinueWithToTask(actualTask, null, onExceptionCallback);
    return actualTask;
}
...

问题是,您将t2任务链接到一个从未运行过的任务的末尾,因为它被设置为OnlyOnFaulted

试试这个:-

        private static Task AddContinueWithToTask(Task task, object userContext, Action<System.Exception> onExceptionCallback = null, Action<Task> onCompletedCallback = null)
        {
            return task
                .ContinueWith(t => {
                    if (t.IsFaulted)
                        ManageException(t, userContext, onExceptionCallback);
                })
                .ContinueWith(t2 => 
                {
                    if (onCompletedCallback != null) onCompletedCallback(t2); 
                })
                .ContinueWith(t => {
                    if (t.IsFaulted)
                        ManageException(t, userContext, onExceptionCallback);
                })
                ;
        }

来自Task.Continuewith 上的MSDN

在当前任务已完成。如果通过continuationOptions参数指定的条件为如果未满足,则继续任务将被取消,而不是按计划执行。