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

WPF中Task.Run导致数据重复入库如何处理及如何终止Task.Run任务

问题1:数据库重复插入数据原因
  • 首先你的Task用法存在根本错误:所有数据库插入逻辑都被包裹在Dispatcher.Invoke中,相当于后台线程启动后立刻切回UI线程执行全部耗时操作,Task.Run完全没有起到后台异步执行的作用,UI仍会卡顿。
  • 重复插入的核心原因是未等待异步任务执行完成就执行后续逻辑,产生了竞态条件:
    你调用Task.Run后没有加await等待保存完成,UI线程会立刻执行后续的GridView刷新逻辑,将旧数据重新绑定到GridView。而后台线程拿到的Gridview.Items是动态集合,遍历的时候会同时取到绑定前后的两份数据,合计600条全部插入。
  • 额外逻辑缺陷:MessageBox.Show("Save Done")在保存操作还未启动时就弹出,和实际执行进度完全不符,同时未做按钮点击防抖,快速点击按钮会触发多次插入任务。
问题2:终止Task.Run启动的任务

使用CancellationTokenSource实现任务的主动终止,步骤如下:

  1. 在窗口类中声明CancellationTokenSource类型的全局字段,用于存储取消令牌源
  2. 每次启动保存任务前初始化令牌源,将令牌传入Task.Run的委托中
  3. 在循环插入的每一步检查令牌的取消状态,收到取消信号后立刻终止循环
  4. 需要终止任务时,调用CancellationTokenSource.Cancel()方法即可
修复后完整代码
// 窗口类全局字段
private CancellationTokenSource _saveCts;

private async void SaveItem(object sender, RoutedEventArgs e)
{
    // 取消上一次未完成的保存任务
    _saveCts?.Cancel();
    _saveCts = new CancellationTokenSource();
    var cancelToken = _saveCts.Token;

    // 提前复制要保存的数据,避免动态集合变化导致重复插入
    var itemsToSave = Gridview.Items.Cast<ItemsModel>().ToList();
    if (itemsToSave.Count == 0) return;

    var saveBtn = sender as Button;
    saveBtn.IsEnabled = false;
    PleaseWaittxt.Visibility = Visibility.Visible;

    try
    {
        bool saveSuccess = await Task.Run(() =>
        {
            var controller = new Controller();
            foreach (var item in itemsToSave)
            {
                // 检测是否收到取消信号
                if (cancelToken.IsCancellationRequested)
                    return false;

                bool addResult = controller.AddItem(item);
                if (!addResult)
                {
                    Dispatcher.Invoke(() => MessageBox.Show("Add failed"));
                    return false;
                }
                Console.WriteLine("Add true to Items");
            }
            return true;
        }, cancelToken);

        if (saveSuccess && !cancelToken.IsCancellationRequested)
        {
            MessageBox.Show("Save Done");
            // 保存完成后再刷新列表
            Gridview.ItemsSource = new Controller().GetAllItems();
        }
    }
    catch (OperationCanceledException)
    {
        MessageBox.Show("Save operation cancelled");
    }
    finally
    {
        // 恢复UI状态
        PleaseWaittxt.Visibility = Visibility.Hidden;
        saveBtn.IsEnabled = true;
        _saveCts.Dispose();
        _saveCts = null;
    }
}

如果需要主动终止任务,只需要在取消按钮的点击事件等位置调用_saveCts?.Cancel()即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 12:24:02