繁体   English   中英

BackgroundWorker上的方法冻结了GUI

[英]Method on BackgroundWorker is freezing GUI

我正在尝试运行一种方法,该方法从指定的文件夹获取文件列表,并在DataGridView中表示它。 该方法在BackgroundWorker中运行,因此我希望GUI保持活动状态。 但是它仍然冻结。 这是一个例子:

private void startScan_Click(object sender, EventArgs e)
{
    bckgrFileScanner.RunWorkerAsync();
}

private void bckgrFileScanner_DoWork(object sender, DoWorkEventArgs e)
{
//for each folder in list perform this function, which scans folder and gets all files
    for (int i = 0; i < folderList.Items.Count; i++)
    {
        GetFileList(ref scannedFiles, folderList.Items[i].ToString(), bckgrFileScanner);

    }
 }

public static void GetFileList(ref List<FileInfo> fList, string fPath, BackgroundWorker scanner)      
{
     DirectoryInfo di = new DirectoryInfo(fPath);
     FileInfo[] fi = di.GetFiles();

     foreach (FileInfo fiTemp in fi)
     {                
          if (fiTemp.Name.StartsWith("~$") == false)
          {
              //adds items to list of all scanned files
              fList.Add(fiTemp);
              //reports file name to ProgressChanged method
              scanner.ReportProgress(0, fiTemp);
          }
     }
     DirectoryInfo[] dFolders = di.GetDirectories();

     //use recursion for all subfolders
     foreach (DirectoryInfo d in dFolders)
     {
          GetFileList(ref fList, d.FullName, scanner);
     }
}

private void bckgrFileScanner_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    //userstate is filename, so add it to table
    filesDataGrid.Rows.Add(e.UserState.ToString());  
}

不要在循环的每一行更新reportprogress事件。 相反,每隔2次或更多次迭代调用ReportProgress(最好的方法是计算et步骤,如果有200000,则不要在每一行更新),并在整个过程完成后填充网格(在completed事件中),以更新所有行。 我认为进度仅是为了更新进度,而不是将大量数据填充到控件中,但是我可能是错的:从这里开始

提示对BackgroundWorker类的了解可能比想象的要多。 BackgroundWorker的名称可能表明它比实际要复杂。 有关线程和中止调用的更多详细信息,但是一旦您了解BackgroundWorker只是Windows Forms中线程的结构化“叠加”,就非常直观。 再次执行以下步骤:

首先,使用参数调用RunWorkerAsync。 您可以在BackgroundWorker上将任何参数传递给此方法,包括null。 它只是必须从对象继承,一切都可以做。

其次,运行自定义处理。 您昂贵的代码在DoWork方法中执行。 在程序执行计算时,在此处插入暂停。

第三,完成。 处理完成后,将调用RunWorkerCompleted。 用这种方法,您会收到结果。 这样,您的BackgroundWorker对象将在另一个线程上修改一个对象,并且在完成该操作后会收到该对象。

我认为当需要在短时间内进行太多更新时,它会挂起,甚至调用Apllication.DoEvents()也不总是有效。 我希望这会有所帮助,我知道有点晚了,但是总比没有好;)

这个(简化的)示例对我有用,并且具有一个漂亮的响应式UI:

    BackgroundWorker m_objWorker = new BackgroundWorker();

    public FormBackgroundWorkerExample()
    {
        InitializeComponent();
        m_objWorker.WorkerReportsProgress = true;
        m_objWorker.DoWork += new DoWorkEventHandler(m_objWorker_DoWork);
        m_objWorker.ProgressChanged += new ProgressChangedEventHandler(m_objWorker_ProgressChanged);
    }

    private void btnStart_Click(object sender, EventArgs e)
    {
        m_objWorker.RunWorkerAsync();
    }

    private void m_objWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        //for each folder in list perform this function, which scans folder and gets all files
        for (int i = 0; i <= 100; i++)
        {
            m_objWorker.ReportProgress(i, "FooBar");
            Thread.Sleep(1000);
        }
    }

    private void m_objWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressBar1.Value = e.ProgressPercentage;
        dataGridView1.Rows.Add(e.UserState.ToString()); 
    }

也许这会让您知道您的问题出在哪里?

编辑:您可能想尝试不使用datagrid只是为了尝试解决问题。

可能是因为您锁定了背景工作人员本身。

在您的DoWork方法中

GetFileList(ref scannedFiles, folderList.Items[i].ToString(), bckgrFileScanner);

通常,您应该可以从DoWork方法访问bckgrFileScanner,而直接将其直接称为bckgrFileScanner.ReportProgress(.... ....)

当像您一样传递时,它现在将在后台工作线程上报告进度,该线程现在与UI线程不同。 (拥有bckgrFileScanner的人)

编辑以澄清:

  1. 您的UI线程拥有bckgrFileScanner并触发RunWorkerAsync()
  2. DoWork中发生的事情现在在其自己的线程上。
  3. DoWork线程正在“窃取”变量bckgrFileScanner
  4. 现在,ReportProgress在DoWork线程而不是UI线程上触发

您正在扫描仪上调用ReportProgress 不应该是bckgrFileScanner吗?

编辑

碰巧scandFiles列表数据绑定到UI了吗? 如果是这样,对列表的更改将导致UI更新。

可能的原因:

我认为您只想报告文件名:

//reports file name to ProgressChanged method
scanner.ReportProgress(0, fiTemp.Name);

或者DoWork中的folderList是一个UI控件:

for (int i = 0; i < folderList.Items.Count; i++)
{
    GetFileList(ref scannedFiles, folderList.Items[i].ToString(), bckgrFileScanner);
}

为什么不将文件夹列表传递给RunWorkerAsync方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM