等待任务取消永久等待

问题描述:

我有一个表单,我在其中启动一个任务来加载内容。如果用户单击取消,则当然需要取消该任务。但似乎我做错了什么。该表单永远不会关闭并持续等待任务:等待任务取消永久等待

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,那么你会得到这个行为 –

你被击中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()'事情...... –

您没有使用取消令牌。这种模式称为合作任务取消 - 消费者和执行代码都需要为取消活动提供服务。这有助于您在取消后保持正确的状态。

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

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

    cancelToken.ThrowIfCancellationRequested(); 

    ... // do some more work 
}