TaskCompletionSource 用法

TaskCompletionSource usage

如果取消完成源,我会在结果变量中收到什么?

async void SomeMethod()
{
   .....
   Run();
   var result = await GetResult();
   .....
}

Task<SomeResult> GetResult()
{
    return myCompletionSource.Task;
}

TaskCompletionSource myCompletionSource;

void Run()
{
     myCompletionSource= new TaskCompletionSource();
     TriggerSomeLongLastingLogicWhichWillCallCallBackBelow();

}

void SomeCallback()
{
     if (someCondition)
     {
         myCompletionSource.SetResult(<someResult>);
     }
     else
     {
         myCompletionSource.SetCancelled();
     }
}

我不太确定这种做法是否正确。

  1. 换句话说,依赖任务状态而不是使用状态变量为 "someresult" 创建包装器是一个好习惯吗?
  2. 如何处理取消的任务?我不喜欢回调,也不喜欢 ContinueWith 的解决方案,我可以在其中分析任务状态。

What would I receive in result variable if completion source was cancelled?

您的代码在等待已取消的任务时将抛出 OperationCancelledException。所以永远不会设置结果变量。

您可以使用 try/catch 块处理异常:

async Task SomeMethod()
{
   try
   {
       .....
       Run();
       var result = await GetResult();
   }
   catch(OperationCancelledException)
   {
       // handle cancelled operation
   }
}

此外,SomeMethod 应该 return a Task 因为 void returning async 方法通常只适用于事件处理程序,因为他们必须 return void。我在博客上简要介绍了它 here

一般来说,如果您希望某个操作可取消,您可以传入一个 CancellationToken,该操作必须检查该操作并将其传递给它启动的其他操作。所以你把它一直传递到链下并进入你的回调。

您还可以使用 CancellationToken 注册一个回调,在令牌被取消时取消 TaskCompletionSource,这样您就不需要在您的方法中执行它。

void Run()
{   
     var cts = new CancellationTokenSource();
     var myCompletionSource= new TaskCompletionSource();
     cts.Token.Register(() => myCompletionSource.SetCancelled());

     TriggerSomeLongLastingLogicWhichWillCallCallBackBelow(cts.Token);         
}

void SomeCallback(CancellationToken token)
{       
     // do some work
     ....

     token.ThrowIfCancellationRequested();

     if (someCondition)
     {
         myCompletionSource.SetResult(<someResult>);
     }
     else
     {
         myCompletionSource.SetException(new Exception("error occcured"));
     }
}