随笔 - 435  文章 - 0  评论 - 111  阅读 - 62万 

BackgroundWorker是在内部使用了线程池的技术;同时,在Winform 或WPF编码中,它还给工作线程和UI线程提供了交互的能力。

Thread和ThreadPool默认都没有提供这种交互能 力,而BackgroundWorker却通过事件提供了这种能力。这种能力包括:报告进度、支持完成回调、取消任务、暂停任务等。

一般而言,无特殊需要的,优先考虑使用标准的backgroundworker
再加上线程池,多数问题都能应付了
只有特别需要精确控制或性能的才用thread

BackgroundWorker会在主线程之外,另开一个后台线程,

我们可以把一些处理放在后台线程中处理,完成之后,后台线程会把结果传递给主线程,同时结束自己。

如果要显示进度,比如我们希望走马灯式更新label,就要把 bw.WorkerReportsProgress = true;

如果要支持中途取消, 则把bw.WorkerSupportsCancellation = true; 

复制代码
        private void button3_Click(object sender, EventArgs e)
        {
            using (BackgroundWorker bw = new BackgroundWorker())
            {
                bw.WorkerReportsProgress = true;
                bw.ProgressChanged += bw_ProgressChanged;
                bw.RunWorkerCompleted += bw_RunWorkerCompleted;
                bw.DoWork += bw_DoWork;

                //允许用户指定显示数据的范围呢!所以需要把100作为参数传递给计算过程
                bw.RunWorkerAsync(100); 
            }

        }
        //这时返回了主线程,所以可以直接使用UI控件了
        void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            //修改进度条的显示。
            //this.progressBarSum.Value = e.ProgressPercentage;

            //如果有更多的信息需要传递,可以使用 e.UserState 传递一个自定义的类型。
            //这是一个 object 类型的对象,您可以通过它传递任何类型。
            //我们仅把当前 sum 的值通过 e.UserState 传回,并通过显示在窗口上。
            string message = e.UserState.ToString();
            label1.Text = message;
        }
        //e.Argument=bw.RunWorkerAsync("Hello World")的参数
        void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("bw_DoWork");
            BackgroundWorker bgWorker = sender as BackgroundWorker;
            //这里的操作是在另一个线程上完成的,不应该操作UI
            //在这里执行耗时的运算。

            int endNumber = 0;
            if (e.Argument != null)
            {
                endNumber = (int)e.Argument;
            }

            for (int i = 0; i <= endNumber; i++)
            {
                bgWorker.ReportProgress(i, "current num:" + i.ToString());
                Thread.Sleep(50); //为了方便演示
            }
            
        }
        //这时后台线程已经完成,并返回了主线程,所以可以直接使用UI控件了
        void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("bw_RunWorkerCompleted");
            if (e.Error == null)
                lblMsg.Text = "正常结束";
            else
                lblMsg.Text = "异常结束"+ e.Error.Message;
        }
复制代码

 

BackGroundWorker的数量和你CPU的核数有关, 比如我CPU是4核的,我开5个线程就经常会出现4个线程先执行2秒后,再执行第5个线程.

并不是线程越多越好, 还有开多个线程,通常要把bw.WorkerReportsProgress打开, 因为我们通常希望的是CPU分片执行, 先执行线程1一段时间,再切换到线程2..3.4.5...

而且界面上通常要显示出每个进程的进度,那么在BW_DoWork里执行一部分任务(比如整个任务可以分成10份,每次执行1份),执行完之后 bgWorker.ReportProgress

 

1
Task.WhenAll 和Task.WaitAll的区别: WaitAll是阻塞的. WhenAll是不阻塞的,通常后面跟上ContinueWith
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[DllImport("winmm.dll")]
       protected static extern int timeGetTime();
 
       static void Main(string[] args)
       {
 
           RabbitMQClient mQClient = new RabbitMQClient("localhost");
           int BeginTime = timeGetTime();
           //1个线程Msg Rate = 125/s, 10个线程300 /s,20个线程400/s,40个线程850/s,100个线程的890/s
           //大于40个线程没有用了,还是不能提高Msg Rate
           Task[] ts = new Task[2];
           for (int k = 0; k < 2; k++)
           {
               int aaa = timeGetTime();
               ts[k]= Task.Run(() =>
               {
                    
                   IModel Channel = mQClient.GetChannel(); //每个线程建立一个通道,RabbitMQClient6不稳定,版本5就没问题
                   for (int i = 0; i < 100 * 1; i++)
                   {
                       mQClient.Send("IDG", "Hello World!" + i, Channel);
                       mQClient.Receive("IDG", Channel);
 
                   }
                    
                   if (Channel != null)
                       Channel.Close();
 
               }).ContinueWith((s)=> { Console.WriteLine(s.Id+ " task Use Time=" + (timeGetTime() - aaa) / 1000f); });
               //t.Wait();
               
           }
           Task.WhenAll(ts).ContinueWith((s)=> { Console.WriteLine("WhenAll Use Time=" + (timeGetTime() - BeginTime) / 1000f); });
           Task.WaitAll(ts);
           Console.WriteLine(" Use Time=" + (timeGetTime() - BeginTime) / 1000f);
 
           Console.ReadLine();
       }

  

 

 

Task 和BackgroundWorker的区别  Task.Run vs BackgroundWorker, Round 5: Reporting Progress (stephencleary.com)

参考: https://www.cnblogs.com/sparkdev/p/5906272.html

https://www.cnblogs.com/luminji/archive/2011/05/13/2044801.html

https://www.cnblogs.com/happy555/archive/2007/11/07/952315.html

https://www.cnblogs.com/grenet/archive/2011/12/21/2289014.html

我所知道的.NET异步  https://www.cnblogs.com/scy251147/archive/2012/03/03/2378477.html

支持暂停操作的Backgroundworker  https://www.cnblogs.com/chenxizhang/archive/2010/03/13/1685209.html

 

posted on   Gu  阅读(2149)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?
点击右上角即可分享
微信分享提示