zoukankan      html  css  js  c++  java
  • 浅谈C#多线程与UI响应

    www.educity.cn     发布者:shenywww                    来源:网络转载                     发布日期:2014年10月06日                                     文章评论                   发表文章            



     

      一.C#多线程概述

      1.后台循环任务,少量UI更新:例如批量上传文件,并提供进度。这种情况使用BackgroundWorker组件是非常好的选择。

      2.耗时的后台任务:这里的耗时任务是指一个时间较长的任务,并且不能精确获取进度,如:调用一个远程WebService接口。这种情况可以开两个线程,一个工作,一个更新UI(不能提供进度,只能显示动画表示系统在运行中)。www.educity.cn     发布者:shenywww                    来源:网络转载                     发布日期:2014年10月06日                                     文章评论                   发表文章            



     

      一.C#多线程概述

      1.后台循环任务,少量UI更新:例如批量上传文件,并提供进度。这种情况使用BackgroundWorker组件是非常好的选择。

      2.耗时的后台任务:这里的耗时任务是指一个时间较长的任务,并且不能精确获取进度,如:调用一个远程WebService接口。这种情况可以开两个线程,一个工作,一个更新UI(不能提供进度,只能显示动画表示系统在运行中)。

      3.耗时的UI任务:当工作压力集中在UI响应上时,可以在工作者线程中增加延时,从而让UI线程获得响应时间。整个工作的总体时间会增加,但用户响应效果会好很多。

      二.后台的循环任务,少量UI更新

      这种情况使用BackgroundWorker组件是最好的选择。(详见附一)

      三.后台耗时任务

      在后台执行一个不可分解的耗时任务,需要进行界面更新,以便让客户看上去程序有所响应。这种情况下,UI线程一般也不知道工作线程何时结束,所以一般执行循环任务,当工作线程结束后,关闭UI线程就可以了。

         Threaduithread=null;     privatevoidbtnStart_Click(objectsender,EventArgse)     {     uithread=newThread(newThreadStart(this.UpdateProgressThread));     uithread.Start();     Threadworkthread=newThread(newThreadStart(this.DoSomething));     workthread.Start();     }     privatevoidDoSomething()     {     Thread.Sleep(5000);     uithread.Abort();     MessageBox.Show("workend");     }     privatevoidUpdateProgressThread()     {     for(inti=0;i<10000;i++)     {     Thread.Sleep(100);     this.Invoke(newAction(this.UpdateProgress),i);     }     }     privatevoidUpdateProgress(intv)     {     this.progressBar1.Value=v;     } 

      这里只要注意一点:线程调用的方法都不能访问用户控件,必须通过委托调用Form的方法来实现界面更新。

      四.耗时的UI任务

      当整个工作压力集中在UI响应上时,可以在工作者线程中增加延时,从而让UI线程获得响应时间。整个工作的总体时间会增加,但用户响应效果会好很多。

         privatevoidFormInitForm_Load(objectsender,EventArgse)     {     this.listView1.Items.Clear();     Threadworkthread=newThread(newThreadStart(this.DoSomething));     workthread.Start();     }     privatevoidDoSomething()     {     for(inti=0;i<30;i++)     {     this.Invoke(newAction(this.LoadPicture),i);     Thread.Sleep(100);     }     }     privatevoidLoadPicture(inti)     {     stringstringtext=string.Format("Item{0}",i);     ListViewItemlvi=newListViewItem(text,0);     this.listView1.Items.Add(lvi);     Thread.Sleep(200);//模拟耗时UI任务,非循环,不可分解     } 

      五.补充

      1.Invoke和BeginInvoke

      在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,正确的做法是将工作线程中涉及更新界面的代码封装为一个方法,通过Invoke或者BeginInvoke去调用,两者的区别就是一个导致工作线程等待,而另外一个则不会。

      而所谓的“一面响应操作,一面添加节点”永远只能是相对的,使UI线程的负担不至于太大而以,因为界面的正确更新始终要通过UI线程去做,我们要做的事情是在工作线程中包揽大部分的运算,而将对纯粹的界面更新放到UI线程中去做,这样也就达到了减轻UI线程负担的目的了。

      2.Application.DoEvent

      在耗时的循环的UI更新的方法中,插入Application.DoEvent,会使界面获得响应,Application.DoEvent会调用消息处理程序。

         privatevoidbutton2_Click(objectsender,EventArgse)     {     for(inti=0;i<30;i++)     {     stringstringtext=string.Format("Item{0}",i);     ListViewItemlvi=newListViewItem(text,0);     this.listView1.Items.Add(lvi);     Thread.Sleep(200);     for(intj=0;j<10;j++)     {     Thread.Sleep(10);     Application.DoEvents();     }     }     }     3.Lock     lock(object)     {     }     等价与     try     {     Monitor.Enter(object);     }     finally     {     Monitor.Exit(object)     } 

      附一:

      BackgroundWorker组件使用说明

      一.概述

      BackgroundWorker是·NET2.0提供的一个多线程组件,在应用程序中使用,可以非常简单方便地实现UI控件通信,并自动处理多线程冲突问题。

      二.基本属性

      1.WorkerReportsProgress,bool:是否允许报告进度;

      2.WorkerSupportsCancellation,bool:是否允许取消线程。

      3.CancellationPending,bool,get:读取用户是否取消该线程。

      三.基本事件

      1.DoWork:工作者线程

      2.RunWorkerCompleted:线程进度报告

      3.ProgressChanged:线程结束报告

      四.基本方法

      1.RunWorkerAsync():启动工作者线程;

      2.CancelAsync():取消工作者线程;

      3.ReportProgress(int);报告进度

      五.代码

         //启动     privatevoidbtnStart_Click(objectsender,EventArgse)     {     this.btnStart.Enabled=false;     this.btnStop.Enabled=true;     this.backgroundWorker.RunWorkerAsync();     }     //通知线程停止     privatevoidbtnStop_Click(objectsender,EventArgse)     {     this.backgroundWorker.CancelAsync();     }     //工作者线程     privatevoidbackgroundWorker_DoWork(objectsender,DoWorkEventArgse)     {     for(inti=0;i<150;i++)     {     if(backgroundWorker.CancellationPending)//查看用户是否取消该线程     {     break;     }     System.Threading.Thread.Sleep(50);//干点实际的事     backgroundWorker.ReportProgress(i);//报告进度     }     }         //线程进度报告     privatevoidbackgroundWorker_ProgressChanged(objectsender,ProgressChangedEventArgse)     {     this.progressBar1.Value=e.ProgressPercentage*100/150;     }     //线程结束报告     privatevoidbackgroundWorker_RunWorkerCompleted(objectsender,RunWorkerCompletedEventArgse)     {     this.btnStart.Enabled=true;     this.btnStop.Enabled=false;     } 

      3.耗时的UI任务:当工作压力集中在UI响应上时,可以在工作者线程中增加延时,从而让UI线程获得响应时间。整个工作的总体时间会增加,但用户响应效果会好很多。

      二.后台的循环任务,少量UI更新

      这种情况使用BackgroundWorker组件是最好的选择。(详见附一)

      三.后台耗时任务

      在后台执行一个不可分解的耗时任务,需要进行界面更新,以便让客户看上去程序有所响应。这种情况下,UI线程一般也不知道工作线程何时结束,所以一般执行循环任务,当工作线程结束后,关闭UI线程就可以了。

         Threaduithread=null;     privatevoidbtnStart_Click(objectsender,EventArgse)     {     uithread=newThread(newThreadStart(this.UpdateProgressThread));     uithread.Start();     Threadworkthread=newThread(newThreadStart(this.DoSomething));     workthread.Start();     }     privatevoidDoSomething()     {     Thread.Sleep(5000);     uithread.Abort();     MessageBox.Show("workend");     }     privatevoidUpdateProgressThread()     {     for(inti=0;i<10000;i++)     {     Thread.Sleep(100);     this.Invoke(newAction(this.UpdateProgress),i);     }     }     privatevoidUpdateProgress(intv)     {     this.progressBar1.Value=v;     } 

      这里只要注意一点:线程调用的方法都不能访问用户控件,必须通过委托调用Form的方法来实现界面更新。

      四.耗时的UI任务

      当整个工作压力集中在UI响应上时,可以在工作者线程中增加延时,从而让UI线程获得响应时间。整个工作的总体时间会增加,但用户响应效果会好很多。

         privatevoidFormInitForm_Load(objectsender,EventArgse)     {     this.listView1.Items.Clear();     Threadworkthread=newThread(newThreadStart(this.DoSomething));     workthread.Start();     }     privatevoidDoSomething()     {     for(inti=0;i<30;i++)     {     this.Invoke(newAction(this.LoadPicture),i);     Thread.Sleep(100);     }     }     privatevoidLoadPicture(inti)     {     stringstringtext=string.Format("Item{0}",i);     ListViewItemlvi=newListViewItem(text,0);     this.listView1.Items.Add(lvi);     Thread.Sleep(200);//模拟耗时UI任务,非循环,不可分解     } 

      五.补充

      1.Invoke和BeginInvoke

      在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,正确的做法是将工作线程中涉及更新界面的代码封装为一个方法,通过Invoke或者BeginInvoke去调用,两者的区别就是一个导致工作线程等待,而另外一个则不会。

      而所谓的“一面响应操作,一面添加节点”永远只能是相对的,使UI线程的负担不至于太大而以,因为界面的正确更新始终要通过UI线程去做,我们要做的事情是在工作线程中包揽大部分的运算,而将对纯粹的界面更新放到UI线程中去做,这样也就达到了减轻UI线程负担的目的了。

      2.Application.DoEvent

      在耗时的循环的UI更新的方法中,插入Application.DoEvent,会使界面获得响应,Application.DoEvent会调用消息处理程序。

         privatevoidbutton2_Click(objectsender,EventArgse)     {     for(inti=0;i<30;i++)     {     stringstringtext=string.Format("Item{0}",i);     ListViewItemlvi=newListViewItem(text,0);     this.listView1.Items.Add(lvi);     Thread.Sleep(200);     for(intj=0;j<10;j++)     {     Thread.Sleep(10);     Application.DoEvents();     }     }     }     3.Lock     lock(object)     {     }     等价与     try     {     Monitor.Enter(object);     }     finally     {     Monitor.Exit(object)     } 

      附一:

      BackgroundWorker组件使用说明

      一.概述

      BackgroundWorker是·NET2.0提供的一个多线程组件,在应用程序中使用,可以非常简单方便地实现UI控件通信,并自动处理多线程冲突问题。

      二.基本属性

      1.WorkerReportsProgress,bool:是否允许报告进度;

      2.WorkerSupportsCancellation,bool:是否允许取消线程。

      3.CancellationPending,bool,get:读取用户是否取消该线程。

      三.基本事件

      1.DoWork:工作者线程

      2.RunWorkerCompleted:线程进度报告

      3.ProgressChanged:线程结束报告

      四.基本方法

      1.RunWorkerAsync():启动工作者线程;

      2.CancelAsync():取消工作者线程;

      3.ReportProgress(int);报告进度

      五.代码

         //启动     privatevoidbtnStart_Click(objectsender,EventArgse)     {     this.btnStart.Enabled=false;     this.btnStop.Enabled=true;     this.backgroundWorker.RunWorkerAsync();     }     //通知线程停止     privatevoidbtnStop_Click(objectsender,EventArgse)     {     this.backgroundWorker.CancelAsync();     }     //工作者线程     privatevoidbackgroundWorker_DoWork(objectsender,DoWorkEventArgse)     {     for(inti=0;i<150;i++)     {     if(backgroundWorker.CancellationPending)//查看用户是否取消该线程     {     break;     }     System.Threading.Thread.Sleep(50);//干点实际的事     backgroundWorker.ReportProgress(i);//报告进度     }     }         //线程进度报告     privatevoidbackgroundWorker_ProgressChanged(objectsender,ProgressChangedEventArgse)     {     this.progressBar1.Value=e.ProgressPercentage*100/150;     }     //线程结束报告     privatevoidbackgroundWorker_RunWorkerCompleted(objectsender,RunWorkerCompletedEventArgse)     {     this.btnStart.Enabled=true;     this.btnStop.Enabled=false;     } 
  • 相关阅读:
    HDU 3681 Prison Break 越狱(状压DP,变形)
    POJ 2411 Mondriaan's Dream (状压DP,骨牌覆盖,经典)
    ZOJ 3471 Most Powerful (状压DP,经典)
    POJ 2288 Islands and Bridges (状压DP,变形)
    HDU 3001 Travelling (状压DP,3进制)
    POJ 3311 Hie with the Pie (状压DP)
    POJ 1185 炮兵阵地 (状压DP,轮廓线DP)
    FZU 2204 7
    POJ 3254 Corn Fields (状压DP,轮廓线DP)
    ZOJ 3494 BCD Code (数位DP,AC自动机)
  • 原文地址:https://www.cnblogs.com/IT-haidong/p/4643865.html
Copyright © 2011-2022 走看看