2017-05-30 169 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都用於較早的點擊。 –