BackgroundWorker上的方法是冻结GUI

时间:2011-04-01 07:03:57

标签: c# .net user-interface backgroundworker freeze

我正在尝试运行一个从指定文件夹获取文件列表并在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());  
}

5 个答案:

答案 0 :(得分:3)

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

  

提示您可能比您对BackgroundWorker的了解更多   类。 BackgroundWorker的名称可能表明它更多   比实际情况复杂。有关线程的更多细节   并且中止调用,但是一旦你理解了BackgroundWorker就是这样   Windows窗体中的线程的结构“叠加”,它是相当的   直观。以下是步骤:

     

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

     

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

     

第三,它结束了。处理完成后,RunWorkerCompleted   叫做。在此方法中,您将收到结果。这样,你的   BackgroundWorker对象修改另一个线程上的对象   完成后接收它。

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

答案 1 :(得分:1)

这个(简化的)示例适用于我,并且具有可爱的响应式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()); 
    }

也许这会让你知道你的错误在哪里?

编辑:你可能想在没有数据网格的情况下尝试它来尝试找出问题。

答案 2 :(得分:1)

可能是因为你正在锁定背景工作者。

在您的DoWork方法中

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

Normaly你应该可以从你的DoWork方法访问bckgrFileScanner,并直接将其称为bckgrFileScanner.ReportProgress(.... ....)

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

修改澄清:

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

答案 3 :(得分:0)

您正在扫描仪上呼叫ReportProgress。不应该是bckgrFileScanner

修改

scanFiles列表是否有可能数据绑定到UI?如果是这样,对列表的更改会导致UI更新。

答案 4 :(得分:0)

可能的原因:

我想你只想报告文件名:

//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方法。