如何在使用backgroundWorker的过程中停止实现

时间:2016-09-06 02:30:39

标签: c# loops backgroundworker cycle

因此该代码采用递增数字流的值和随机起始编号。如果start1值大于start2,我希望显示与文本框对应的行,否则为另一行。

问题是我不能停止程序,直到不满足给定的循环次数。按钮只是在植入过程中挂起。我知道发生这种情况的原因是一个循环。现在我正在尝试使用backgroundWorker停止它,但我得到了相同的结果,取消按钮挂起相同的方式。

我想使用位于backgroundWorker1_ProgressChanged内的backgroundWorker的代码。我真的不太明白这里发生了什么。也许你帮我弄清楚我做错了什么:

using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace XX_8_0
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            backgroundWorker1.WorkerReportsProgress = true;
            backgroundWorker1.WorkerSupportsCancellation = true;
        }

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

        private void cancelAsyncButton_Click(object sender, EventArgs e)
        {
            if (backgroundWorker1.WorkerSupportsCancellation == true)
            {
                backgroundWorker1.CancelAsync();
            }
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            for (int i = 1; i <= 10; i++)
            {
                if (worker.CancellationPending == true)
                {
                    e.Cancel = true;
                    break;
                }
                else
                {
                    System.Threading.Thread.Sleep(500);
                    worker.ReportProgress(i * 10);
                }
            }
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        { 
            var random = new Random();
            var start1 = random.Next(0, 100);
            var start2 = random.Next(0, 100);
            var incrementor1 = start1 > 50 ? -1 : 1;
            var incrementor2 = start2 > 50 ? -1 : 1;
            var cV1 = start1;
            var cV2 = start2;

            for (var i = 0; i < 1000; i++)
            {
                if (cV1 == 101) incrementor1 = -1;
                if (cV1 == 0) incrementor1 = 1;

                if (cV2 == 101) incrementor2 = -1;
                if (cV2 == 0) incrementor2 = 1;

                if (cV1 > cV2)
                {
                    textBox1.AppendText("ID: (" + i + ") CV1: (1): [" + cV1 + "] CV2: (0) [" + cV2 + "]\n");
                }
                else
                {
                    textBox1.AppendText("ID: (" + i + ") CV1: (0): [" + cV1 + "] CV2: (1) [" + cV2 + "]\n");
                }

                cV1 += incrementor1;
                cV2 += incrementor2;
            }
        }
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled == true)
            {
                resultLabel.Text = "Canceled!";
            }
            else if (e.Error != null)
            {
                resultLabel.Text = "Error: " + e.Error.Message;
            }
            else
            {
                resultLabel.Text = "Done!";
            }
        }    
    }
}

2 个答案:

答案 0 :(得分:3)

注意:我认为您的示例可能不是BackgroundWorker可以做的好例子。基本上你的代码只是睡了一会然后报告进度,在另一个线程中几乎没用。

backgroundWorker1_ProgressChanged加上1000 textBox1.AppendText()非常重,因此可能需要一些时间。

  

按钮只是在植入过程中挂起。我知道发生这种情况的原因是循环。

当您考虑在UI线程上执行backgroundWorker1_ProgressChanged时,无论您在backgroundWorker1_ProgressChanged返回后在取消上点击的次数是多少,都不会处理。所有UI处理都必须由UI线程完成。应该指出后台工作线程在此期间也被挂起,不幸的是它是代码中唯一测试取消的部分。即使ReportProgress是异步的,您仍然需要UI线程来处理取消按钮点击事件并将工作标记为CancellationPending

我建议您不要在backgroundWorker1_ProgressChanged中一次性报告,也可以考虑报告将项目添加到textBox1作为批次

通过这种方式,您希望充分利用您的消息泵并使您的应用程序更具响应性。

将您的backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)更改为使用批次

var builder = new StringBuilder();

for (var i = 0; i < 1000; i++)
{
    if (cV1 == 101) incrementor1 = -1;
    if (cV1 == 0) incrementor1 = 1;

    if (cV2 == 101) incrementor2 = -1;
    if (cV2 == 0) incrementor2 = 1;

    if (cV1 > cV2)
    {
        builder.Append("ID: (" + i + ") CV1: (1): [" + cV1 + "] CV2: (0) [" + cV2 + "]\n");
    }
    else
    {
        builder.Append("ID: (" + i + ") CV1: (0): [" + cV1 + "] CV2: (1) [" + cV2 + "]\n");
    }

    cV1 += incrementor1;
    cV2 += incrementor2;
}

textbox1.AppendText(builder.ToString());

答案 1 :(得分:0)

由于你的ProgressChanged处理程序非常忙于每ReportProgress次调用更新UI大约1000次,它将使用所有UI线程CPU时间,并且该线程将永远无法处理取消按钮,因为它是已经太忙了。

鉴于您使用了BackgroundWorker,您应该在ProgressChanged处理程序中移动大部分DoWork,并在后台线程中删除该睡眠。

方式,您使用BackgroundWorker没有任何意义。 UI线程应该尽可能少,后台线程应该尽可能多。在你的情况下睡在后台线程没有意义,因为后台工作不依赖于已经过去的时间。

移动代码后,您需要将数据发送回UI线程。但这很容易,因为ReportProgress的重载允许将任何.NET对象传递给UI线程(假设后台工作程序是从UI线程创建的,通常就是这种情况)。另一方面,您可以简单地将用户数据转换为适当的类型,并使用该信息将相应的文本附加到文本框中。

通过改进代码,您应该能够更快地处理这些数据。顺便说一句,StringBuilder也应该用来构建字符串。实际上,它应该会对UI性能产生巨大影响,因为您不经常更新1000次UI文本。