请教BackgroundWorker的用法,关于ISbusy

6lilu9 2019-07-31 09:13:07
首次接触到BackgroundWorker,结果很不顺利,我把情况简单模拟如下:

①加载窗体前,自动生成10000个随机数的求和Sum
②在Form1_Load时,加载Sum并显示。

注:其实这个问题的关键就是这句代码“while (this.bgWorker.IsBusy) { };”
我加上这句代码的目的就是保证BackgroundWorker已运行完成(换句话说,Sum是10000个随机数的和;而不是9999个,或者9998个.....)
,但我加上这句代码后才惊奇地发现陷入了死循环,bgWorker.IsBusy怎么一直是true呢


请问我的问题是什么原因,如何解决,谢谢。

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

namespace WindowsFormsApp16
{
public partial class Form1 : Form
{

private BackgroundWorker bgWorker = new BackgroundWorker();

private int SUM=0;
public Form1()
{
InitializeComponent();
bgWorker.DoWork += bgWorker_DoWork;
bgWorker.RunWorkerCompleted += bgWorker_WorkerCompleted;
bgWorker.RunWorkerAsync();
}

public void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i <= 10000; i++)
{
SUM += new Random().Next(i,100001);
System.Console.WriteLine("第"+i.ToString() +"次");
}
}

public void bgWorker_WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("已完成bgWorker_WorkerCompleted");
MessageBox.Show(this.bgWorker.IsBusy.ToString() );
}

private void Form1_Load(object sender, System.EventArgs e)
{
//while (this.bgWorker.IsBusy) { };
MessageBox.Show("已完成bgWorker_DoWork,把SUM赋值给UI界面");
label1.Text = SUM.ToString();
}
}
}
...全文
582 10 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
引用 3 楼 6lilu9 的回复:
[quote=引用 1 楼 exception92 的回复:] WorkerCompleted 这就表示任务已完成。将sum的赋值放到completed事件里,注意跨线程操作。
这知道有另外的实现方式,我现在想弄明白其中的原理: 是不是这样理解呢: 现在有两个进程①Form②BackgroundWorker ①的进程事件包括load show ②的进程事件包括DoWork,completed 而两个进程无法保证实时通信,所以在①进程事件Load中访问②进程的状态是不可行的?[/quote] 他们两个根本不是什么进程,都是类,被调用的方式不同,且在同一个进程内。通信也是线程之间的通信。
XBodhi. 2019-07-31
  • 打赏
  • 举报
回复
WorkerCompleted
事件里做,

注意 Random 的种子,否则会随机出都是一样的数字。
hztltgg 2019-07-31
  • 打赏
  • 举报
回复
试了下,用法不对,没有机会修改IsBusy状态,你放在按钮事件里也可以,放在Load事件里就只能加Application.DoEvents();了

        private void Form1_Load(object sender, System.EventArgs e)
        {

            while (this.bgWorker.IsBusy) {
                Application.DoEvents();
            };

            MessageBox.Show("已完成bgWorker_DoWork,把SUM赋值给UI界面");
            label1.Text = SUM.ToString();
        }
hztltgg 2019-07-31
  • 打赏
  • 举报
回复
    while (this.backgroundWorker1.IsBusy)
    {
        progressBar1.Increment(1);
        // Keep UI messages moving, so the form remains 
        // responsive during the asynchronous operation.
        Application.DoEvents();
    }
6lilu9 2019-07-31
  • 打赏
  • 举报
回复
引用 1 楼 exception92 的回复:
WorkerCompleted 这就表示任务已完成。将sum的赋值放到completed事件里,注意跨线程操作。
这知道有另外的实现方式,我现在想弄明白其中的原理: 是不是这样理解呢: 现在有两个进程①Form②BackgroundWorker ①的进程事件包括load show ②的进程事件包括DoWork,completed 而两个进程无法保证实时通信,所以在①进程事件Load中访问②进程的状态是不可行的?
  • 打赏
  • 举报
回复
简便异步实现方式,了解async/await关键字。
  • 打赏
  • 举报
回复
WorkerCompleted 这就表示任务已完成。将sum的赋值放到completed事件里,注意跨线程操作。
6lilu9 2019-07-31
  • 打赏
  • 举报
回复
引用 5 楼 hztltgg 的回复:
试了下,用法不对,没有机会修改IsBusy状态,你放在按钮事件里也可以,放在Load事件里就只能加Application.DoEvents();了
谢谢。
6lilu9 2019-07-31
  • 打赏
  • 举报
回复
引用 8 楼 github_36000833 的回复:
先给出一种解决方案,后面再谈原因:

        public void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            int sum = 0;
            for (int i = 0; i <= 10000; i++)
            {
                sum += new Random().Next(i, 100001);
                System.Console.WriteLine("第" + i.ToString() + "次");
            }
            e.Result = sum;
        }

        public void bgWorker_WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            label1.Text = "" + e.Result;
            MessageBox.Show("已完成bgWorker_WorkerCompleted");
        }

        private void Form1_Load(object sender, System.EventArgs e)
        {
            label1.Text = "计算中...";
        }
引用
... 但我加上这句代码后才惊奇地发现陷入了死循环,bgWorker.IsBusy怎么一直是true呢
原因是BackgroundWorker要使用UI线程来改变IsBusy属性。 如果UI线程被一个while (this.bgWorker.IsBusy) { }阻塞了,BackgroundWorker得不到任何机会来改变IsBusy属性。 好奇者可以参考BackgroundWorker的源码(跳过也无妨): https://referencesource.microsoft.com/#system/compmod/system/componentmodel/BackgroundWorker.cs 其中isRunning = false;是在private void AsyncOperationCompleted中完成的,而AsyncOperationCompleted要扔到asyncOperation上下文中执行,而asyncOperation上下文,就是你调用bgWorker.RunWorkerAsync()时的线程(UI线程 )。 知道原因,解决方案就很容易了,就是不要阻塞UI线程。 因为RunWorkerCompleted事件通知会在工作完成后发出, 我们可以直接在bgWorker_WorkerCompleted得到总计,并更新lable1。
谢谢,回答地不能再详细了,十二分感谢。
github_36000833 2019-07-31
  • 打赏
  • 举报
回复
先给出一种解决方案,后面再谈原因:

        public void bgWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            int sum = 0;
            for (int i = 0; i <= 10000; i++)
            {
                sum += new Random().Next(i, 100001);
                System.Console.WriteLine("第" + i.ToString() + "次");
            }
            e.Result = sum;
        }

        public void bgWorker_WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            label1.Text = "" + e.Result;
            MessageBox.Show("已完成bgWorker_WorkerCompleted");
        }

        private void Form1_Load(object sender, System.EventArgs e)
        {
            label1.Text = "计算中...";
        }
引用
... 但我加上这句代码后才惊奇地发现陷入了死循环,bgWorker.IsBusy怎么一直是true呢
原因是BackgroundWorker要使用UI线程来改变IsBusy属性。 如果UI线程被一个while (this.bgWorker.IsBusy) { }阻塞了,BackgroundWorker得不到任何机会来改变IsBusy属性。 好奇者可以参考BackgroundWorker的源码(跳过也无妨): https://referencesource.microsoft.com/#system/compmod/system/componentmodel/BackgroundWorker.cs 其中isRunning = false;是在private void AsyncOperationCompleted中完成的,而AsyncOperationCompleted要扔到asyncOperation上下文中执行,而asyncOperation上下文,就是你调用bgWorker.RunWorkerAsync()时的线程(UI线程 )。 知道原因,解决方案就很容易了,就是不要阻塞UI线程。 因为RunWorkerCompleted事件通知会在工作完成后发出, 我们可以直接在bgWorker_WorkerCompleted得到总计,并更新lable1。

111,098

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧