开发者

Method on BackgroundWorker is freezing GUI

I'm trying to run a method that gets file list from specified folders and represents it in DataGridView. The method is ran in BackgroundWorker, so I'm expecting GUI to stay active. But it still freezes. Here's an example:

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)
{开发者_JAVA技巧
    //userstate is filename, so add it to table
    filesDataGrid.Rows.Add(e.UserState.ToString());  
}


Do not update your reportprogress event at each row of your loop. Instead, call the ReportProgress every 2 or more iteration (the best way is to compute et step to not update at each row if you have 200000) And fill the grid when the whole process has finished (in the completed event), to update all the rows. I think progress is meant to update a progress only, not fill a bunch of data into a control but i may be wrong : from here :

Tips You probably know more than you think about the BackgroundWorker class. BackgroundWorker has a name that might indicate it is more complex than it really is. There are many more details about threading and abort calls, but once you understand that BackgroundWorker is just a structural "overlay" to threads in Windows Forms, it is quite intuitive. Here are the steps again:

First, call RunWorkerAsync with an argument. You can pass any argument to this method on BackgroundWorker, including null. It simply must inherit from object, which everything does.

Second, custom processing is run. Your expensive code is executed in the DoWork method. Insert pause here as your program does its calculations.

Third, it finishes. When your processing is done, RunWorkerCompleted is called. In this method, you receive the result. In this way, your BackgroundWorker object modifies an object on another thread, and you receive it when it is done.

I think it hangs up when too much updates in a short time are required, and even calling Apllication.DoEvents() does not work all the times. I hope it will help, a little late i know, but it's better than never ;)


This (simplified) example works for me and has a lovely responsive 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()); 
    }

Perhaps this'll give you an idea where yours is going wrong?

Edit: You might want to try it without the datagrid just to try and isolate the problem.


Might be because you are locking the backgroundworker itself.

In your DoWork method

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

Normaly you should have access to bckgrFileScanner from your DoWork Method, and just call it directly as bckgrFileScanner.ReportProgress( .... .... )

When passing it like you do, it will now report progress on the background workers thread, which is now not the same as the UI thread. (who owns bckgrFileScanner)

Edit To clarify:

  1. Your UI thread owns bckgrFileScanner and fires RunWorkerAsync()
  2. The stuff that happens in DoWork is now on its own thread.
  3. The DoWork thread is "stealing" the variable bckgrFileScanner
  4. Now the ReportProgress fires on the DoWork thread, instead of the UIs thread


You are calling ReportProgress on scanner. Shouldn't be that bckgrFileScanner?

EDIT

Is by any chance the scannedFiles list databound to the UI? If so a change to the list causes a UI update.


Possible reasons:

I think you just want to report the filename:

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

Or the folderList in DoWork is a UI control:

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

Why not pass the list of folders to the RunWorkerAsync method.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜