如何将任务的取消状态传播到后续任务任务、如何将、状态

2023-09-09 21:09:58 作者:我可以和你相亲嘛

我使用任务并行库在我的应用程序。我有一个任务(姑且称之为DoSomething的),这可能会被取消。任务是否出现故障,取消或成功完成,我必须连接到该任务,执行一些清理工作的延续。

I am using the Task Parallel Library in my application. I have a Task (let's call it "DoSomething") which may be canceled. Whether the task is faulted, canceled, or completes successfully, I have a continuation attached to that task which performs some cleanup.

在code,会启动这个任务,我想回到一个任务对象,其状态(故障,取消运行完成)反映了DoSomething的任务的状态,但它是这个任务我返回不能反映很重要这种状态,直到继续执行任务。

In the code that launches this task, I want to return a Task object whose status (faulted, canceled, ran to completion) reflects the status of the DoSomething task, however it's important that this task I return not reflect this status until the continuation task executes.

下面是一个例子:

public Task Start(CancellationToken token)
{
    var doSomethingTask = Task.Factory.StartNew(DoSomething
                                                , token);

    var continuationTask = doSomethingTask.ContinueWith
                (
                 (antecedent) =>
                     {
                         if (antecedent.IsFaulted || antecedent.IsCanceled)
                         {
                             //Do failure-specific cleanup
                         }

   //Do general cleanup without regard to failure or success
                      }
                 );

//TODO: How do I return a Task obj which Status reflect the status of doSomethingTask,
//but will not transition to that status until continuationTask completes?
}

我可以用一个TaskCompletionSource,但这似乎缺憾。任何其他的想法?

I could use a TaskCompletionSource, but that seems kludgy. Any other ideas?

推荐答案

我觉得 TaskCompletionSource 其实是非常适合这个场景。即你试图返回工作为你的工作完成的信号,但手动控制的时间和方式的任务报告其状态。你可以很容易隐藏需要这种有像这样的扩展方法的锅炉板:

I think that TaskCompletionSource is actually ideal for this scenario. I.e. you are trying to return a Task as a signal of completion of your work, but manually control when and how that task reports its status. You could easily hide the boiler plate required for this with an extension method like this:

public static Task<T> WithCleanup<T>(this Task<T> t, Action<Task<T>> cleanup) {
    var cleanupTask = t.ContinueWith(cleanup);
    var completion = new TaskCompletionSource<T>();
    cleanupTask.ContinueWith(_ => {
        if(t.IsCanceled) {
            completion.SetCanceled();
        } else if(t.IsFaulted) {
            completion.SetException(t.Exception);
        } else {
            completion.SetResult(t.Result);
        }
    });
    return completion.Task;
}

和调用它是这样的:

var doSomethingTask = Task.Factory
  .StartNew<object>(DoSomething, token)
  .WithCleanup(Cleanup);

唯一真正需要注意的是,你不能用普通的老式工作,因为没有非泛型 TaskCompletionSource 。

The only real caveat is that you can't do this with plain old Task since there is no non-generic TaskCompletionSource.