等待任务取消永远等待

时间:2017-08-04 08:16:11

标签: c# task cancellation

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

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);
}

我传递了Token子方法,因为循环实际上就在那里......

2 个答案:

答案 0 :(得分:4)

您正在通过等待它并调用Task.Wait()来阻止UI线程上的死锁。不惜一切代价避免使用Task.Wait

在异步延续中提供取消的结果,如下所示:

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

这是async void唯一可以接受的时间。

我最喜欢的异步代码人物Stephen Cleary提供了一篇精彩的博客文章,解释了为什么你应该避免Task.WaitTask.Result作为阻止机制 - Don't Block on Async Code

为了它的价值,当我取消时,我从不等待任务完成。我立即回复取消,让任务在后台完成。这为用户提供了响应式UI体验。如果我需要从取消的任务中获得结果,我会将一些用户界面工作用于沟通,等待操作结束'用户点击取消后。

答案 1 :(得分:0)

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

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

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

    cancelToken.ThrowIfCancellationRequested();

    ... // do some more work
}