0

所以我一直在阅读很多不鼓励使用 Application.DoEvents() 的文章,甚至说它永远不应该被使用,但我似乎无法为我的场景找到一个好的替代方案......应用程序我正在研究的有一个方法,当主 GUI 表单首次启动时,该方法由 this.Shown 事件调用。该方法执行一些需要大约一分钟时间的工作,因此相同的方法还创建了一个本质上是自定义进度条的表单。请记住,此过程当前是单线程的,因此当此方法运行时,主 GUI 和进度条变得无响应。如果用户在此期间单击任何地方,屏幕将变为空白。所以我正在努力将这个方法所做的一些工作放在 BackgroundWorker 线程中。这是我想出的:

private BackgroundWorker Bgw = new BackgroundWorker();
private int LoadPercentage = 0;

    //this sub is executed on the main UI thread
    public void RunBgw()
    {
        bool StopThread = false;
        //this object should be created in this method and needs to be updated as the child thread is doing work
        MyCustomDialog dialog = new MyCustomDialog();  

        dialog.UpdateProgress(0, "My message");

        dialog.Show();
        this.Invalidate();
        this.Refresh();

        //critical properties to set if you want to report progress/be able to cancel the operation
        Bgw.WorkerSupportsCancellation = true;
        Bgw.WorkerReportsProgress = true;

        //add handlers to Bgw so events will fire
        Bgw.DoWork += new DoWorkEventHandler(Bgw_DoWork);
        Bgw.ProgressChanged += new ProgressChangedEventHandler(Bgw_ProgressChanged);
        Bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Bgw_RunWorkerCompleted);

        //fire off thread
        Bgw.RunWorkerAsync();

        while (Bgw.IsBusy == true) 
        {
            if (BW.CancellationPending == true) 
            {
                StopThread = true;
                break;
            }
            Application.DoEvents();

            if(LoadPercentage == 10) 
            {
                dialog.UpdateProgress(LoadPercentage, "Still working...");
                this.Invalidate();
                this.Refresh();
            }
            if(LoadPercentage == 50) 
            {
                dialog.UpdateProgress(LoadPercentage, "Halfway done...");
                this.Invalidate();
                this.Refresh();
            }
            // etc...

            //slow down loop so it doesnt take up all the CPU
            Thread.Sleep(200);
        }

        if(!StopThread) {
            //continue with something else.
        }
    }

    private void Bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker BgwLocal = sender as BackgroundWorker;

        if ((BgwLocal.CancellationPending == true))
        {
            e.Cancel = true;
            break;
        }
        else
        {
            TimeConsumingWork();
            BgwLocal.ReportProgress(10); //report progress back to the main UI thread
            TimeConsumingWork();
            BgwLocal.ReportProgress(15, SomeGuiIcon); //pass back specific gui icon
            TimeConsumingWork();
            BgwLocal.ReportProgress(50); 

            // etc...  
        }
    }

    private void Bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        LoadPercentage = e.ProgressPercentage; //set current percentage of progress

        if(LoadPercentage == 15)
        {
            GuiIcon1 = (Icon)e.UserState; //set gui icon
        }
    }

    private void Bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if ((e.Cancelled == true))
        {
            //error handling
        }
        else if (!(e.Error == null))
        {
            //error handling
        }
        else
        {
            //success
        }
    }

除了错误处理被证明是困难和混乱之外,一切都运行良好。在更新主线程中的现有对象时,是否有更好的线程工作方式?

谢谢阅读。

4

1 回答 1

3

您不应该使用以下代码阻塞 UI 线程:

while (Bgw.IsBusy == true) { ... }

相反,允许RunBgw()返回给调用者。使用 BackgroundWorker 上的事件来了解它何时完成。具体来说

Bgw.ProgressChanged += new ProgressChangedEventHandler(Bgw_ProgressChanged);

通过调用报告进度

Bgw_ProgressChanged

Bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Bgw_RunWorkerCompleted);

原因

Bgw_RunWorkerCompleted

在 BackgroundWorker 完成时调用。

从内部更新进度条Bgw_ProgressChanged

Windows UI 是事件驱动的。您的代码没有使用事件来控制程序执行。

于 2014-01-09T23:41:42.000 回答