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

加载数据至DataGridView时窗口冻结,为何线程方案无效?

问题描述

在按钮点击事件中,前两个无UI更新的方法通过Task.Run执行时不会导致窗口卡顿,但执行loadFoldersToGrid方法时窗口会冻结(加载完成后恢复正常),即便使用Task.Run结合BeginInvoke仍未解决,相关代码如下:

private async void btnSearch_Click(object sender, EventArgs e)
{
    btnFolder.Enabled = false;
    btnSearch.Enabled = false;
    btnStop.Enabled = true;

    try
    {
        var foldersTask = Task.Run(() => GetAccessibleDirectories(txtFolderPath.Text, chkSubFolders.Checked));
        var folders = await foldersTask;
        var BF = await Task.Run(() => getFolderdetails(folders));

        await Task.Run(() => this.BeginInvoke(new Action(() => loadFoldersToGrid(BF)))); // Runs on UI thread
    }
    finally
    {
        btnFolder.Enabled = true;
        btnSearch.Enabled = true;
        btnStop.Enabled = false;
    }
}
private async void loadFoldersToGrid(BrowserFolders BF)
{
    int i = 0;
    long MBcon = 1048576;
    long MB, Total = 0;

    dgFolders.Rows.Clear();
    dgFolders.Rows.Add(BF.folders.Count);

    foreach (var rec in BF.folders)
    {
        dgFolders.Rows[i].Cells[0].Value = i;
        dgFolders.Rows[i].Cells[1].Value = rec.folderPath;
        dgFolders.Rows[i].Cells[2].Value = rec.fileCount;
        long folderSize = GetFolderSize(rec.folderPath);
        string megabytes = (folderSize / MBcon).ToString("F1");
        dgFolders.Rows[i].Cells[3].Value = megabytes;
        dgFolders.Rows[i].Cells[4].Value = rec.fileCreated.ToString("MM/dd/yyyy");
        Total = Total + folderSize;
        i += 1;
    }

    txtFolderCount.Text = BF.folders.Count.ToString();
    txtSize.Text = string.Format("{0:N0} MB", Total / MBcon);
}

请问该问题的原因是什么?异步任务为何无法解决?

原因分析
  • loadFoldersToGrid本质仍在UI线程执行同步阻塞操作:外层的Task.Run完全多余,BeginInvoke本身就是把委托投递到UI线程消息队列执行,最终loadFoldersToGrid的所有代码还是在UI线程运行。
  • GetFolderSize是耗时IO操作,直接阻塞UI线程:循环中每次调用GetFolderSize都会同步读取磁盘,占用UI线程时间片,导致窗口无法响应刷新、用户输入等事件,表现为冻结。
  • 假异步的async方法:loadFoldersToGrid标记了async但内部没有任何await操作,方法会同步执行到底,不会释放UI线程。
解决方法

1. 把耗时IO操作移到后台线程执行

提前在后台计算好所有文件夹的大小,避免在UI线程做磁盘读取:

private async void btnSearch_Click(object sender, EventArgs e)
{
    btnFolder.Enabled = false;
    btnSearch.Enabled = false;
    btnStop.Enabled = true;

    try
    {
        var folders = await Task.Run(() => GetAccessibleDirectories(txtFolderPath.Text, chkSubFolders.Checked));
        var BF = await Task.Run(() => getFolderdetails(folders));
        
        // 后台计算所有文件夹大小
        var foldersWithSize = await Task.Run(() => 
            BF.folders.Select(rec => new 
            {
                Rec = rec,
                Size = GetFolderSize(rec.folderPath)
            }).ToList()
        );
        
        // 仅在UI线程做纯界面更新
        loadFoldersToGrid(foldersWithSize);
    }
    finally
    {
        btnFolder.Enabled = true;
        btnSearch.Enabled = true;
        btnStop.Enabled = false;
    }
}

2. 优化UI更新逻辑,减少频繁刷新

修改loadFoldersToGrid,只做纯UI操作,同时暂停DataGridView绘制以减少刷新开销:

// 移除不必要的async,当前无异步操作
private void loadFoldersToGrid(List<dynamic> foldersWithSize)
{
    int i = 0;
    long MBcon = 1048576;
    long Total = 0;

    dgFolders.SuspendLayout();
    dgFolders.Rows.Clear();
    dgFolders.Rows.Add(foldersWithSize.Count);

    foreach (var item in foldersWithSize)
    {
        var rec = item.Rec;
        long folderSize = item.Size;
        string megabytes = (folderSize / MBcon).ToString("F1");
        
        dgFolders.Rows[i].Cells[0].Value = i;
        dgFolders.Rows[i].Cells[1].Value = rec.folderPath;
        dgFolders.Rows[i].Cells[2].Value = rec.fileCount;
        dgFolders.Rows[i].Cells[3].Value = megabytes;
        dgFolders.Rows[i].Cells[4].Value = rec.fileCreated.ToString("MM/dd/yyyy");
        
        Total += folderSize;
        i++;
    }

    txtFolderCount.Text = foldersWithSize.Count.ToString();
    txtSize.Text = string.Format("{0:N0} MB", Total / MBcon);
    
    dgFolders.ResumeLayout();
}

3. 推荐使用数据绑定替代逐行赋值

创建ViewModel类整理数据,直接绑定到DataGridView,进一步优化UI性能:

// 定义ViewModel
public class FolderViewModel
{
    public int Index { get; set; }
    public string Path { get; set; }
    public int FileCount { get; set; }
    public string SizeMB { get; set; }
    public string CreatedDate { get; set; }
}

// 修改UI更新方法
private void loadFoldersToGrid(List<FolderViewModel> folderViewModels)
{
    dgFolders.SuspendLayout();
    // 需提前在设计时为DataGridView列绑定对应DataPropertyName
    dgFolders.DataSource = folderViewModels;
    dgFolders.ResumeLayout();
    
    long totalSize = folderViewModels.Sum(vm => long.Parse(vm.SizeMB.Split(' ')[0]));
    txtFolderCount.Text = folderViewModels.Count.ToString();
    txtSize.Text = string.Format("{0:N0} MB", totalSize);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 03:15:01