【问题标题】:TaskCompletionSource usageTaskCompletionSource 用法
【发布时间】:2015-11-05 11:44:10
【问题描述】:

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

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 的解决方案,我可以在其中分析任务状态。

【问题讨论】:

    标签: c# async-await taskcompletionsource


    【解决方案1】:

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

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

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

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

    另外,SomeMethod 应该返回一个Task 作为void 返回async 方法通常只适用于事件处理程序,因为它们必须返回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"));
         }
    }
    

    【讨论】:

    • 您的博文中关于async void 的内容有误。来自async void 方法的异常不能被调用者捕捉到,但不会被忽视。如何处理此类异常取决于应用程序的类型,但例如在 Windows 窗体应用程序中,它们的处理方式将与任何其他未捕获的异常相同。它们在事件处理程序之外很有用,尽管事件处理程序确实是最常见的用途。未观察到的任务处理程序在您博客的示例中运行的唯一原因是您忘记等待 Task.Run 的结果。
    • 感谢您的反馈,我会审查该部分。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多