2017-08-04 155 views
1

我有一個表單,我在其中啓動一個任務來加載內容。如果用戶單擊取消,則當然需要取消該任務。但似乎我做錯了什麼。該表單永遠不會關閉並持續等待任務:等待任務取消永久等待

public partial class Designer : Form 
{ 
    private CancellationTokenSource _cancellationTokenSource; 
    private Task _loadTask; 

    private async void Designer_Shown(object sender, EventArgs e) 
    { 
     _cancellationTokenSource = new CancellationTokenSource(); 
     try 
     { 
      _loadTask= Workbench.Instance.CurrentPackage.LoadObjects(_cancellationTokenSource.Token); 
      await _loadTask; 
     } 
     catch (Exception ex) 
     { 
      Debug.Print(ex.ToString()); 
     } 
    } 

    private void btnCancel_Click(object sender, EventArgs e) 
    { 
     _cancellationTokenSource.Cancel(); 
     _loadTask.Wait(); //Waits forever 
     this.DialogResult = DialogResult.Cancel; 
     this.Close(); 
    } 
} 

我的錯在哪裏?

編輯 代碼LoadObjects()

public Task LoadObjects(CancellationToken cancelToken) 
{ 
    return Task.Run(() => 
    { 
     LoadParameters(cancelToken); 
     LoadConditionChecks(cancelToken); 
     LoadConditonRules(cancelToken); 
     LoadOperations(cancelToken); 
    }, cancelToken); 
} 

我通過令牌的子方法因爲迴路實際上是有...

+2

任務取消需要運行,以檢查它是否被取消的任務。這聽起來好像不這樣做,但我不知道Workbench是什麼。 – Equalsk

+1

您是否控制'Workbench.Instance.CurrentPackage.LoadObjects'的代碼。你確定它是尊重取消標記嗎? – Gusdor

+0

代碼中接受'CancellationToken'的地方應該有一個像CancellationToken.IsCancellationRequested或CancellationToken.ThrowIfCancellationRequested這樣的取消檢查如果它不是隻有atb,那麼你會得到這個行爲 –

回答

3

你被擊中UI線程死鎖等待它並致電Task.Wait()。不惜一切代價避免Task.Wait

服務在一個異步執行的取消,這樣的結果:

private async void btnCancel_Click(object sender, EventArgs e) 
{ 
    _cancellationTokenSource.Cancel(); 
    await _loadTask; 
    this.DialogResult = DialogResult.Cancel; 
    this.Close(); 
} 

這是唯一的時間async void是可接受的。

我最喜歡的異步代碼的人斯蒂芬·克利裏提供了一個輝煌的博客文章,解釋爲什麼你應該避免Task.WaitTask.Result作爲支撐機制 - Don't Block on Async Code


爲了什麼它的價值,當我取消,我從來不等待任務完成。我立即回覆取消,並讓任務在後臺完成。這爲用戶提供了響應式UI體驗。如果我需要從已取消的任務中獲得結果,我將在用戶單擊取消後進行一些UI工作以進行通信'等待結束操作'。

+0

這就是它......在我用google找到的所有例子中,他們都做了'.Wait()'事情...... –

0

您沒有使用取消令牌。這種模式稱爲合作任務取消 - 消費者和執行代碼都需要爲取消活動提供服務。這有助於您在取消後保持正確的狀態。

的解決方案 - 更新LoadX()方法:

void LoadParameters(cancelToken) 
{ 
    ... // do some work 

    cancelToken.ThrowIfCancellationRequested(); 

    ... // do some more work 
}