【问题标题】:Background operation and blocking main form后台操作和阻塞主窗体
【发布时间】:2010-10-08 22:16:55
【问题描述】:

这是我的场景: 在一个表单上,我有目录列表、按钮和控件来显示多行文本。 在一个循环中,我尝试查找每个目录中的所有文件并删除它们。 删除文件后,我想将文本添加到多行控件。 我的问题是,当添加文本时,我无能为力。表单被阻止,如果我尝试做任何事情,它就会停止响应。 使用 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();
            }
        }
    }

我怎样才能改进它以使其不阻塞表单?

【问题讨论】:

  • 我看不到你在哪里实例化你的工人。你打电话给RunWorkerAsync();
  • 我调用 backgroundWorker1.RunWorkerAsync(GetRowsToDelete());当用户按下按钮 GetRowsToDelete() 时返回包含要删除的目录的表。 BackgroundWorker 工作正常并删除文件,但显示信息会阻止我的表单
  • 闯入调试器并查看线程阻塞的位置。是否有订阅 memoEdit1 的 TextChanged 事件的处理程序?

标签: c# backgroundworker


【解决方案1】:

您过于频繁地调用 ReportProgress。每秒执行大约 1000 次以上,UI 线程就会被它无法跟上的请求淹没。它不会做它的正常职责,包括绘制控件和响应鼠标和键盘。它看起来冻结了。当 UI 更新代码变得更昂贵时,情况会变得更糟,当 TextBox 中已经有很多文本时更新文本会变得很慢。

在 BGW 停止运行后,诊断仍然看到 UI 冻结了一段时间,正在清空调用请求队列中的积压,然后在队列最终清空时突然活跃地跳回来。

您需要限制调用 BeginInvoke() 的速率。比每 50 毫秒一次更频繁地调用它更有意义,人类无法感知除此之外的差异。在 List 中收集信息,这样您就可以减少 BeginInvoke() 的频率。如果您的工作人员能够比 UI 线程能够跟上的速度更快地产生结果,这仍然不能完全保证。在这种情况下,放慢工人的速度将是一个解决办法。使用 Invoke 而不是 BeginInvoke 很容易。

【讨论】:

  • 谢谢 :) 我在调用 backgroundWorker1.ReportProgress 之前添加了 Thread.Sleep(10) 并且效果很好。我还会将结果添加到 List,这样 backgroundWorker1.ReportProgress 的调用频率就会降低
【解决方案2】:

如果这个worker异步运行,那么你可以有一个响应你的表单。

另外,问题:

  1. 您正在另一个函数中运行循环 - 它使操作无响应。

  2. 您甚至没有检查用户是否要取消(只是我想说的一点) - 在 foreach 循环内处理 DoWorkEventArgsCancel 属性。

将函数CzyscPliki的代码移到backgroundWorker1_DoWork中(反正太小了)。

编辑:

如果您不想将代码移动到DoWork 事件处理程序中,那么最好使用Thread 以获得更多控制权。我不是这方面的专家,但你会发现很多关于如何实现的代码。

【讨论】:

  • 但是已经从 backgroundWorker1_DoWork 调用了 CzyscPliki,所以它已经在另一个线程上执行了。
  • 这不是重点。问题是在外部函数中循环,而不是外部函数没有被调用。
  • function CzyscPliki 也在其他地方使用,即使我在 backgroundWorker1_DoWork 中移动它的代码,它仍然不起作用。你能给我一个将使用后台工作人员的示例代码吗?可以说,在 while(true) 循环中,工作人员正在将表单文本更改为当前时间。这当然需要取消
  • 好的..这只是msdn链接..我现在没有现成的代码...msdn.microsoft.com/en-us/library/…
  • 它仍然无法正常工作。问题在于文本控制。如果我向它添加文本,它会阻止我的表单。如果我评论添加文本,它可以正常工作。我曾尝试使用 BackgroundWorker.ProgressChanged 但没有运气。会不会是控件有问题?
猜你喜欢
  • 2011-04-11
  • 2018-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多