You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF异步更新UI时取消任务引发死锁的解决方法

解决WPF异步任务取消时的死锁问题

核心问题分析

你遇到的死锁确实是因为updateMoviesTask?.Wait()阻塞了UI线程:UI线程在等待UpdateMovies任务完成,而UpdateMovies中await异步操作完成后,需要回到UI线程执行后续的UI更新逻辑,但UI线程已经被Wait()卡死,双方互相等待导致死锁。

解决方案

1. 替换Wait()为await,避免阻塞UI线程

属性的setter不能标记为async,所以我们需要把取消、等待、重新执行的逻辑抽离到一个异步方法中,通过非阻塞的方式调用它。

2. 优化CancellationTokenSource的管理

避免在任务未完成时就Dispose旧的CancellationTokenSource,应该在任务取消并完成后再清理资源,同时在异步操作中传入CancellationToken,让取消操作更及时。

修改后的完整代码

private CancellationTokenSource _cts;
private Task _updateMoviesTask;
private GetProviderResponse _providerId;

public GetProviderResponse ProviderId
{
    get => _providerId;
    set
    {
        if (_providerId == value) return;
        _providerId = value;
        OnPropertyChanged(nameof(ProviderId));
        // 调用异步处理逻辑,不阻塞UI线程
        _ = HandleProviderChangeAsync();
    }
}

private async Task HandleProviderChangeAsync()
{
    // 取消当前任务
    _cts?.Cancel();
    try
    {
        // 等待当前任务完成(非阻塞)
        if (_updateMoviesTask != null)
        {
            await _updateMoviesTask;
        }
    }
    catch (OperationCanceledException)
    {
        // 任务被取消是预期情况,无需处理异常
    }
    finally
    {
        // 清理旧的CancellationTokenSource
        _cts?.Dispose();
    }

    // 创建新的CancellationTokenSource并启动新任务
    _cts = new CancellationTokenSource();
    _updateMoviesTask = UpdateMoviesAsync(_cts.Token);
}

private async Task UpdateMoviesAsync(CancellationToken cancellationToken)
{
    var request = new GetPopularRequest(Country.Germany);

    for (int i = 1; i <= TotalPages; i++)
    {
        // 先检查取消,避免不必要的请求
        cancellationToken.ThrowIfCancellationRequested();
        
        request.Page = i;
        request.ProviderId = ProviderId;

        // 把CancellationToken传入异步方法,让请求可以被及时取消
        var getPopularResponse = await justWatchClient.GetPopularAsync(request, cancellationToken);

        // 执行UI操作(这里确保在UI线程,因为await后默认回到捕获的上下文)
        // do ui stuff here        

        // 再次检查取消
        cancellationToken.ThrowIfCancellationRequested();
    }
}

关键改进点

  • 用await替代Wait():await _updateMoviesTask不会阻塞UI线程,异步操作完成后会自动回到UI线程继续执行,避免死锁。
  • 单独的异步处理方法:HandleProviderChangeAsync封装了取消、等待、重新启动任务的逻辑,解决了属性setter不能异步的问题。
  • 及时取消异步请求:将CancellationToken传入GetPopularAsync,可以在任务取消时直接终止网络请求,而不是等待请求完成后再检查取消状态。
  • 安全清理资源:在任务完成后才Dispose旧的CancellationTokenSource,避免资源未释放或正在使用时被Dispose。
  • 异常处理:捕获OperationCanceledException,处理任务被取消的情况,避免未处理异常导致程序崩溃。

内容的提问来源于stack exchange,提问作者codymanix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 14:22:26