后台操作和阻止主窗体

时间:2010-10-08 13:19:19

标签: c# backgroundworker

这是我的情景: 在表单上,​​我有一个目录列表,按钮和控件,以显示多行文字。 在循环中,我尝试查找每个目录中的所有文件并删除它们。 删除文件时,我想将文本添加到多行控件。 我的问题是,当添加文本时,我无法做任何其他事情。表单被阻止,如果我尝试做任何操作它只是停止响应。 使用BackgroundWorker

删除文件
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
//this is datatable with directories and other info
        MainDataset.CZYSZCZENIEDataTable CZYSZCZENIE = e.Argument as MainDataset.CZYSZCZENIEDataTable;
        CzyscPliki(CZYSZCZENIE, ReportProgress);
    }

private void CzyscPliki(MainDataset.CZYSZCZENIEDataTable CZYSZCZENIE, ReportProgressDel del)
    {
        DirectoryInfo dir = null;
        FileInfo[] files = null;
        bool subfolder = false;
        string katalog = "";
        string maska = "";
        string[] maski = null;
        long total=0;

        string dirS;
        string fileS;
        long fileLen;
//foreach directory to delete
        foreach (DataRow r in CZYSZCZENIE.Rows)
        {
//CanRead - check if row is not deleted or detached
//r["CZYSC"].AsBool() - check if directory should be cleared
            if (r.CanRead() && r["CZYSC"].AsBool())
            {
                subfolder = r["PODKATALOGI"].AsBool();
                katalog = r["KATALOG"].AsString().TrimEnd('\\');
                maska = r["MASKA"].AsString();
                if (maska.IsEmpty())
                    maska = "*";
                maski = maska.Split(';');
                dir = new DirectoryInfo(katalog);
                if (dir.Exists)
                {
                    foreach (string s in maski)
                    {
                        files = dir.GetFiles(s, (subfolder ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly));
                        dir.GetFiles();
                        foreach (FileInfo f in files)
                        {
                            dirS = f.Directory.FullName;
                            fileS = f.Name;
                            fileLen = f.Length;
                            try
                            {
                                f.Delete();
                                total += fileLen;
                                if (del != null)
//here is problem: del - delegate to report state
//when it is called it blocks form
                                    del(dirS, fileS, fileLen, total);

                            }
                            catch (Exception ex) 
                            { }
                        }
                    }
                }
            }
        }
    }
//this is the delegate that appends text in multiline control
//memoEdit1 is the control
//ceReportProgress.Checked - check if report should be added
private void ReportProgress(string directory, string file, long size, long totalSize)
    {
        if (memoEdit1.InvokeRequired)
        {
            memoEdit1.BeginInvoke(new Action<string, string, long, long>(ReportProgress), directory, file, size, totalSize);
        }
        else
        {
            if (ceReportProgress.Checked)
            {
                if (file.IsEmpty())
                    memoEdit1.AppendText("\r\nCzyszczenie katalogu " + directory);
                else
                {
                    memoEdit1.AppendText(file);
                    if (size > 0)
                    {
                        if (size > 1048576)
                        {
                            decimal d = size / 1048576;
                            d = decimal.Round(d, 2);
                            memoEdit1.AppendText("\tWielkość : " + d.AsString() + " megabajtów", false);
                        }
                        else if (size > 1024)
                        {
                            decimal d = (decimal)size / (decimal)1024;
                            d = decimal.Round(d, 2);
                            memoEdit1.AppendText("\tWielkość : " + d.AsString() + " kilobajtów", false);
                        }
                        else
                            memoEdit1.AppendText("\tWielkość : " + size.AsString() + " bajtów", false);
                    }
                    if (totalSize > 0)
                    {
                        if (totalSize > 1073741824)
                        {
                            decimal d = (decimal)totalSize / (decimal)1073741824;
                            d = decimal.Round(d, 2);
                            memoEdit1.AppendText("Zwolniono dotychczas : " + d.AsString() + " gigabajtów");
                        }
                        else if (totalSize > 1048576)
                        {
                            decimal d = (decimal)totalSize / (decimal)1048576;
                            d = decimal.Round(d, 2);
                            memoEdit1.AppendText("Zwolniono dotychczas : " + d.AsString() + " megabajtów");
                        }
                        else if (totalSize > 1024)
                        {
                            decimal d = (decimal)totalSize / (decimal)1024;
                            d = decimal.Round(d, 2);
                            memoEdit1.AppendText("Zwolniono dotychczas : " + d.AsString() + " kilobajtów");
                        }
                        else
                            memoEdit1.AppendText("Zwolniono dotychczas : " + totalSize.AsString() + " bajtów");
                    }
                }
//scroll to the end of control
                memoEdit1.ScrollToEnd();
            }
        }
    }

如何改进此功能,使其不会阻止表单?

2 个答案:

答案 0 :(得分:2)

您经常调用ReportProgress。每秒执行超过1000次,并且UI线程充斥着它无法跟上的请求。它无法完成正常工作,包括绘制控件和响应鼠标和键盘。它看起来很冷冻。当UI更新代码变得更加昂贵时,这会变得更糟,当TextBox中已有大量文本变得非常慢时,更新TextBox中的文本。

在BGW停止运行后诊断仍然看到UI冻结一段时间,在清空调用请求队列中的积压工作时,然后在队列最终清空时突然恢复活跃。

您需要限制调用BeginInvoke()的速率。更频繁地称呼它比每50毫秒更频繁一次,人类无法察觉超出这一点的差异。收集列表中的信息&lt;&gt;所以你可以减少BeginInvoke()的频率。如果你的工作人员能够比UI线程能够跟上的速度更快地产生结果,那仍然不能完全保证。在这种情况下,减慢工人的速度将是一个解决方案。使用Invoke而不是BeginInvoke很容易。

答案 1 :(得分:0)

如果此工作程序异步运行,那么您可以拥有一个响应您的表单。

此外,问题:

  1. 您正在另一个函数中运行循环 - 它使操作非反复。

  2. 您甚至没有检查用户是否要取消(只是我想要的一个点) - 在DoWorkEventArgs循环内处理Cancel的{​​{1}}属性。< / p>

  3. 将函数foreach的代码移到CzyscPliki中(无论如何太小)。

    编辑:

    如果您不想将代码移动到backgroundWorker1_DoWork事件处理程序中,那么最好使用DoWork进行更多控制。我不是它的专家,但你会发现很多关于如何实现的代码。