简体   繁体   English

运行没有块主线程的代码

[英]Run code without block main thread

I need to generate n random strings and this process may take a while and block the main thread UI. 我需要生成n个随机字符串,这个过程可能需要一段时间并阻塞主线程UI。 For avoid this and let user use the programm while the process is running I decided to use a backGroundWorker . 为避免这种情况,让用户在进程运行时使用该程序,我决定使用backGroundWorker But it didn't work well and the main thread still is blocked. 但它不能很好地工作,主线程仍然被阻止。 In my DoWork event I have something like this: 在我的DoWork活动中,我有这样的事情:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
           // using Invoke because I change value of some controls, e.g,   ListView, labels and a progressbar
            this.Invoke((Action)delegate
            {
                for (int i = 0; i < nSteps; ++i)
                {
                    string s = getnStr();
                    // ...
                    int progress = (int)(100.0 / nSteps * (i + 1));
                    backgroundWorker1.ReportProgress(progress);
                }
            });
        }

Even although I call ReportProgress() inside the loop, the progressbar only changes its value when the loop is done. 即使我在循环中调用ReportProgress() ,但进度条仅在循环完成时更改其值。

Why is this happening and how can I fix this? 为什么会发生这种情况?我该如何解决这个问题?

Invoke... That's where you get it all wrong. 调用......这就是你弄错的地方。 You're only creating a background worker just so you invoke the code back to the main thread (that's what Invoke does). 您只是创建一个后台工作程序,因此您将代码调用回主线程(这就是Invoke所做的)。

You should actually either use invoke only for the controls access, either modify the controls only in the ReportProgress handler and then make sure you call the ReportProgress method every time when needed. 实际上,您应该仅对控件访问使用invoke,或者仅在ReportProgress处理程序中修改控件,然后确保每次需要时调用ReportProgress方法。

EDIT: Clarification: your problem with the invoke is that you're invoking the UI thread for the entire workload that the background worker was supposed to do. 编辑:澄清:您调用的问题是您正在为后台工作者应该执行的整个工作负载调用UI线程。

The other answer explained the behavior you are seeing. 另一个答案解释了您所看到的行为。 Please read it first. 请先阅读。 Here is how to fix this: 以下是解决此问题的方法:

BackgroundWorker is obsolete because we have Task and await now. BackgroundWorker已经过时,因为我们有Taskawait现在。 It automates a lot. 它自动化很多。 Your code probably should look like this: 您的代码可能应如下所示:

            //In the button click handler:
            for (int i = 0; i < nSteps; ++i)
            {
                await DoSomeWorkAsync();
                int progress = (int)(100.0 / nSteps * (i + 1));
                SetProgressBarValue(progress);
            }

That's really all. 这就是全部。 You need to ensure that DoSomeWorkAsync does not block. 您需要确保DoSomeWorkAsync不会阻止。 It must return a Task . 它必须返回一个Task

Besides reporting progress ReportProgress() method can be used as a generic asynchronous event dispatcher (eg to update text in UI controls): 除了报告进度外,ReportProgress()方法还可以用作通用异步事件调度程序(例如,更新UI控件中的文本):

 for (int i = 0; i < nSteps; ++i)
 {
   string s = getnStr();

   // Update text
   backgroundWorker1.ReportProgress(0, "My text..");    

   // Update progress
   int progress = (int)(100.0 / nSteps * (i + 1));
   backgroundWorker1.ReportProgress(progress);
 }

ProgressChanged event handler would look something like this: ProgressChanged事件处理程序看起来像这样:

void backgroundWorker1_ProgressChanged(object sender,  ProgressChangedEventArgs e)
{
     if (e.UserState != null)
     {
           // Handle text update
           label1.Text = (string)e.UserState;
           return;
     } 

     progressBar1.Value = e.ProgressPercentage;   
}

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

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