【问题标题】:Events not happening in order事件没有按顺序发生
【发布时间】:2018-09-19 12:27:25
【问题描述】:

我正在用 C# 创建一个 WinForms 应用程序。当我单击一个按钮时,应该会发生一定的事件流:

  • 点击按钮
  • 显示标签1
  • 显示标签2
  • 调用函数来解析用户之前输入的字符串(这可能需要一段时间,具体取决于字符串)
  • 显示 listBox1 和 progressBar1
  • backgroundWorker1.RunWorkerAsync
  • backgroundWorker1_DoWork() 做某事 x 次,每次都报告进度
  • backgroundWorker1_ProgressChanged() 更新progressBar1 并向listBox1 添加一个项目
  • backgroundWorker1_RunWorkCompleted() 显示一个消息框,显示“DONE”

但实际情况并非如此。当我跟踪代码并查看表单时,它有几个问题。

  1. label1 和 label2 直到解析完成后才会真正出现。
  2. progressBar1 有时只会在调用 ProgressChanged 时更新。其他时候,它会等到“DONE”被打印出来并立即更新。
  3. 每次调用 progressChange() 时,listBox1 上的垂直滚动条都会变小,因此我可以知道正在添加项目,但直到打印“DONE”之后,项目的文本才会出现。

我是使用 backgroundWorker 的新手,所以我可能只是不明白它应该如何工作。但是显示标签的延迟我根本不明白。当我跟踪代码时没有错误,并且这些行似乎以正确的顺序执行。

有没有人知道可能导致这些问题的原因?我将不胜感激任何帮助或建议。我宁愿不发布我的代码,只是因为有很多,但如果有人需要它来更好地理解,只需 lmk。

编辑:这是代码。

private void button1_Click(object sender, EventArgs e){
    label1.Show();
    label2.Show();
    String errMsg = parseString();
    if (errMsg == ""){
        listBox1.Items.Clear();
        listBox1.Show();

        progressBar1.Maximum = 100;
        progressBar1.Step = 1;
        progressBar1.Value = 0;
        progressBar1.Show();

        backgroundWorker1.DoWork += backgroundWorker1_DoWork;
        backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
        backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
        backgroundWorker1.WorkerReportsProgress = true;
        backgroundWorker1.WorkerSupportsCancellation = true;

        if (backgroundWorker1.IsBusy != true)
        {
            backgroundWorker1.RunWorkerAsync();
        }
    }
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        backgroundWorker1.ReportProgress(1, "Updating Devices");
        for (int i = 0; i < 100; i++)
        {
            //todo: do stuff

            //update progress
            backgroundWorker1.ReportProgress(i, "Device:" + i);
        }
    }

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressBar1.Value = e.ProgressPercentage;
        listBox1.Items.Add(e.UserState);
    }

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        MessageBox.Show("DONE");
    }

【问题讨论】:

  • 请提供代码,方便我们理解。
  • 既然没有代码,我想,你怎么写你的代码就有问题了。标准且正确的方法是从按钮附着处理函数 (更新标签) 启动后台工作程序并结束该函数。这样应该不会出现上述问题。
  • 您的代码过于频繁地调用 ProgressChanged。 UI 线程充斥着调用请求,它将开始燃烧 100% 内核以尝试跟上。并且不会处理其较低优先级的任务,例如重新绘制控件和响应用户输入。你只有人的眼睛可以保持忙碌,这一切都会以每秒超过 25 次更新的速度变得模糊。现在你可能每秒执行超过一千次。
  • 是的,就可以了。你也在烧内存,这是 UI 线程必须烧掉这么多内核的一个重要原因。使用可以记录〜最后一分钟数据的循环缓冲区之类的东西。使用按钮显示该缓冲区的稳定快照。如何显示缓冲区内容的摘要以便用户获得适度的“它正在工作”指示取决于您。

标签: c# winforms backgroundworker


【解决方案1】:

感谢@HansPassant 和@mjwills 的cmets。他们带领我走上了正确的道路,并使这个解决方案成为可能。

最后我决定做两个后台工作者来解决label1和label2直到解析完成后才出现的问题。我用第一个做解析,第二个做“做事”部分。在代码中,您将看到我必须使用 Invoke 来编辑标签,因为该部分现在存在于不同的线程中。

我还意识到调用 ProgressChanged 之前的“做事”并不是立即的。我一直在分段开发,尚未实现该代码,但我知道这些操作至少需要 3 秒才能完成(部分原因是涉及到 ping)。所以现在我在那个循环中放了一个 Sleep(3000) 调用来模拟它的实际行为。这解决了由于耗尽所有内存而导致的奇怪的 progressbar1 和 listbox1 行为。

代码结果如下:

private void button1_Click(object sender, EventArgs e)
    {
        if (backgroundWorker1.IsBusy != true)
        {
            backgroundWorker1.RunWorkerAsync();
        }
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        String errMsg = parseString();
        if (errMsg == "")
        {
            if (listBox1.InvokeRequired)
            {
                listBox1.Invoke(new MethodInvoker(delegate
                {
                    listBox1.Items.Clear();
                    listBox1.Show();
                }));
            }

            if (progressBar1.InvokeRequired)
            {
                progressBar1.Invoke(new MethodInvoker(delegate
                {
                    progressBar1.Maximum = 100;
                    progressBar1.Step = 1;
                    progressBar1.Value = 0;
                    progressBar1.Show();
                }));
            }

            if (backgroundWorker2.IsBusy != true)
            {
                backgroundWorker2.RunWorkerAsync();
            }
        }
        else
        {
            MessageBox.Show(errMsg);
        }
    }

    private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
    {
        backgroundWorker2.ReportProgress(1, "Updating Devices");
        for (int i = 0; i < 100; i++)
        {
            System.Threading.Thread.Sleep(3000);
            //do stuff

            backgroundWorker2.ReportProgress(i, "Device:" + i);
        }
    }

    private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        if (progressBar1.InvokeRequired)
        {
            progressBar1.Invoke(new MethodInvoker(delegate
            {
                progressBar1.Value = e.ProgressPercentage;
            }));
        }
        if (listBox1.InvokeRequired)
        {
            listBox1.Invoke(new MethodInvoker(delegate
            {
                listBox1.Items.Add(e.UserState);
            }));
        }
    }

    private void backgroundWorker2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        MessageBox.Show("DONE");
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-01
    • 1970-01-01
    相关资源
    最近更新 更多