简体   繁体   English

后台操作和阻止主要形式

[英]Background operation and blocking main form

Here is my scenario: On a form I have list of direcotories, Button and control to display multiline text. 这是我的场景:在表单上,​​我有目录列表,按钮和显示多行文本的控件的列表。 In a loop I try to find all files in each directory and delete them. 在一个循环中,我尝试在每个目录中找到所有文件并将其删除。 When file is deleted i want to add text to multiline control. 当文件被删除时,我想向多行控件添加文本。 My problem is that when text is added I can not do anything else. 我的问题是,添加文本后,我无能为力。 Form is blocked and if I try do do anytching it just stops responding. 表单被阻止,如果我尝试进行任何处理,它将停止响应。 Files are deleted using 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();
            }
        }
    }

How can I improve this to make it not blocking the form? 我如何改进它以使其不阻塞表格?

You are calling ReportProgress too often. 您经常调用ReportProgress。 Do it more than about 1000 times per second and the UI thread gets flooded with requests that it cannot keep up with. 每秒执行约1000次以上,UI线程就会被无法满足的请求所淹没。 It won't get around to doing its normal duties, which include painting the controls and responding to the mouse and keyboard. 它无法绕开其正常的工作,包括绘制控件以及响应鼠标和键盘。 It looks frozen. 它看起来冻结了。 This gets worse when the UI update code gets more expensive, updating text in a TextBox when there's already a lot of text in it can get quite slow. 当UI更新代码变得更昂贵时,情况将变得更糟;当其中包含大量文本时,更新TextBox中的文本会变得非常缓慢。

The diagnostic is still seeing the UI frozen for a while after the BGW stops running, working on emptying the backlog in the invoke request queue, then suddenly jumping back alive when the queue is finally emptied. 在BGW停止运行后,诊断程序仍然看到UI冻结了一段时间,正在清空调用请求队列中的积压工作,然后在最终清空队列时突然跳回活动状态。

You need to throttle the rate at which you call BeginInvoke(). 您需要限制调用BeginInvoke()的速率。 It never makes more sense to call it any more frequently than once every 50 milliseconds, a human cannot perceive the difference beyond that. 每50毫秒一次的调用频率再没有比这更有意义的了,人类无法感知到超过此的差异。 Collect the info in a List<> so you can BeginInvoke() a lot less frequently. 将信息收集在List <>中,这样您就可以减少BeginInvoke()的频率。 That's still no complete guarantee if your worker can produce results faster than the UI thread could ever keep up with. 如果您的工作者产生的结果比UI线程所能跟上的速度还快,那还不能完全保证。 In which case slowing down the worker would be a fix. 在这种情况下,降低工人的工作速度是可以解决的。 Easy by using Invoke instead of BeginInvoke. 使用Invoke而不是BeginInvoke可以轻松实现。

If this worker is running asynchronously, then you can have a form which responds to you. 如果此工作程序正在异步运行,则您可以具有一个响应您的表单。

Besides, problems: 此外,问题:

  1. You are running the loop in another function - it makes the operation non-reponsive. 您正在另一个函数中运行循环-它使操作无响应。

  2. You are not even checking if user wants to cancel (just a point i wanted to make) - Handle DoWorkEventArgs 's Cancel property inside the foreach loop. 您甚至没有检查用户是否要取消(这只是我想提出的要点)-在foreach循环中处理DoWorkEventArgsCancel属性。

Move the function CzyscPliki 's code in the backgroundWorker1_DoWork (it's anyway too tiny). 将函数CzyscPliki的代码CzyscPliki backgroundWorker1_DoWork (反正太小了)。

EDIT: 编辑:

If you don't want to move the code into DoWork event handler, then better use Thread for more control. 如果您不想将代码移至DoWork事件处理程序中,则最好使用Thread进行更多控制。 I'm not an expert on it but you will find plenty of code on how to implement so. 我不是专家,但是您会找到很多有关如何实现的代码。

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

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