如何在有backgroundWorker的过程中停止执行

How to stop the implementation during the process with backgroundWorker

因此此代码采用具有随机起始编号的递增数字流的值。如果 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!";
            }
        }    
    }
}

注意:我认为您的示例可能不是 BackgroundWorker 可以做什么的真正好示例。本质上你的代码只是休眠了一会儿然后报告进度,在另一个线程中几乎没有用。


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

Button just hangs during implantation. I understand that the reason why this happens is a loop.

当您考虑 backgroundWorker1_ProgressChanged 在 UI 线程上执行时,无论您在 取消 上进行多少次点击都不会被处理,直到 backgroundWorker1_ProgressChanged returns。所有 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());

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

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

顺便说一下,你用的BackgroundWorker没有任何意义。 UI 线程应该做的越少越好,后台线程应该做的越多越好。在你的情况下,在后台线程中休眠是没有意义的,因为后台工作不依赖于经过的时间。

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

通过改进代码,您应该能够更快地处理该数据。顺便说一下,StringBuilder 也应该用于构建字符串。事实上,它应该对 UI 性能产生巨大影响,因为您更新 UI 文本的频率减少了 1000 次。