2017-05-30 163 views
2

我试图使用this FAQ中描述的取消令牌。这是我最初的想法:使用异步任务取消任务

private async void OnLoginButtonClicked(object sender, EventArgs e) 
{ 
    if (this.cancelToken == null) 
    { 
     this.cancelToken = new CancellationTokenSource(); 
    } 

    try 
    { 
     bool loginSuccess = await AsyncLoginTask(this.cancelToken.Token); 

     if (loginSuccess) 
     { 
      // Show main page 
     } 
    } 
    catch (OperationCanceledException ex) 
    { 
     System.Diagnostics.Debug.WriteLine(ex.Message); 
    } 
    catch (Exception ex) 
    { 
     System.Diagnostics.Debug.WriteLine(ex.Message); 
    } 
    finally 
    { 
     this.cancelToken = null; 
    } 
} 

private async Task<bool> AsyncLoginTask(CancellationToken cancellationToken = default(CancellationToken)) 
{ 
    // Pass the token to HttpClient() 
} 

现在我适应它,这就是结果:

private async void OnLoginButtonClicked(object sender, EventArgs e) 
{ 
    this.cancelToken?.Dispose(); 
    this.cancelToken = new CancellationTokenSource(); 

    try 
    { 
     var ui = TaskScheduler.FromCurrentSynchronizationContext(); 
     var loginTask = Task.Factory.StartNew(async() => 
     { 
      bool loginSuccess = await AsyncLoginTask(this.cancelToken.Token); 
     }, this.cancelToken.Token); 

     var displayResults = loginTask.ContinueWith(resultTask => 
          { 
           // How do I know if the login was successful? 
           // Because AsyncLoginTask() returns bool. 
           System.Diagnostics.Debug.WriteLine("done"); 
          }, 
          CancellationToken.None, 
          TaskContinuationOptions.OnlyOnRanToCompletion, 
          ui); 

     var displayCancelledTasks = loginTask.ContinueWith(resultTask => 
            { 
             System.Diagnostics.Debug.WriteLine("canceled"); 
            }, 
            CancellationToken.None, 
            TaskContinuationOptions.OnlyOnCanceled, ui); 
    } 
    catch (Exception ex) 
    { 
     System.Diagnostics.Debug.WriteLine(ex.Message); 
    } 
} 

问题:

  • 我怎么知道,如果登录成功?因为AsyncLoginTask()返回bool
  • 如何正确创建和销毁令牌以允许多次启动和取消操作?
  • 如何处理任务中的任务? “完成”显示在控制台中,而任务(AsyncLoginTask)尚未完成。
+0

'我怎么知道,如果登录成功?因为AsyncLoginTask()返回bool,所以你可以在该任务中返回'loginSuccess',这会给'loginTask'一个结果。但是......我的问题在于,你为什么要在任务中包装你的登录任务? – Stefan

+0

我的旧代码('AsyncLoginTask')是以这种方式构建的(async/await,返回true/false)。现在我想让用户可以取消操作。在链接的文章中,推荐使用“ContinueWith”任务。所以我包装了我的任务。我应该继续捕捉'OperationCanceledException'吗? – testing

+0

那么,我还没有深入的链接文章,我必须说我不是这方面的专家......但是,由于登录已经有一个选项提供取消令牌,并在稍后的逻辑需要登录结果,一个简单的'await'对我来说似乎更直观。你可以用适当的方式处理这个取消。你能解释你的实际目标吗?为什么你认为这个重构将有助于实现它? – Stefan

回答

2

我试图使用本常见问题中所述的取消标记。

该博客文章使用Dynamic Task Parallelism(StartNewContinueWith)。动态任务并行是指当你有很多CPU绑定操作要做时,你不知道你有多少,直到你已经处理它们(也就是说,你处理的每一个可以添加零个或多个额外的任务处理)。

就你而言,你有一个单一的异步操作。因此,该文章中的方法对您的用例完全错误。你原来的想法更加正确。

你想要做更多这样的:

private async void OnLoginButtonClicked(object sender, EventArgs e) 
{ 
    // Cancel the previous attempt (if any) and start a new one. 
    this.cts?.Cancel(); 
    this.cts = new CancellationTokenSource(); 

    try 
    { 
    bool loginSuccess = await AsyncLoginTask(this.cts.Token); 
    // Resolve race condition where user cancels just as it completed. 
    this.cts.Token.ThrowIfCancellationRequested(); 
    if (loginSuccess) 
    { 
     // Show main page 
    } 
    } 
    catch (OperationCanceledException ex) 
    { 
    System.Diagnostics.Debug.WriteLine(ex.Message); 
    } 
    catch (Exception ex) 
    { 
    System.Diagnostics.Debug.WriteLine(ex.Message); 
    } 
} 

private async Task<bool> AsyncLoginTask(CancellationToken cancellationToken = default(CancellationToken)) 
{ 
    // Pass the token to HttpClient() 
} 
+0

感谢您的回答。如果再次按下登录按钮,取消标记必须新建? – testing

+0

是的,因为任何已经存在的CTS都用于较早的点击。 –