用Timer还是用BackgroundWorker实现定时更新画面的功能

用Timer还是用BackgroundWorker实现定时更新画面的功能

标签: timerobjectrandomstring工作class
  974人阅读  评论(0)  收藏  举报

第一次发文章,水平有限,如有错误欢迎大家指出。

 

声明:

1,测试环境为vs2008, .net framework 3.5

2,此次我使用的Timer是 System.Windows.Forms 命名空间下的 Timer 组件,另外两个Timer我没试验。

3,我没有用工具箱添加Timer和BackgroundWorker

 

首先

介绍一下画面

总共有3个Form

主画面

 

点击第一个图标弹出下面的画面

 

Timer画面

 

点击第二个图标弹出下面的画面

 

BackgroundWorker

 

 

两个画面很类似。

实现的是同一个功能。

 

主画面功能不详述。

 

先说第一个画面

 

[c-sharp]  view plain  copy
  1. using System;  
  2. using System.Windows.Forms;  
  3. namespace TimerAndWorker  
  4. {  
  5.     /// <summary>  
  6.     /// System.Windows.Forms.Timer示例  
  7.     /// 运行时会出现画面假死  
  8.     /// </summary>  
  9.     public partial class FrmTimer : Form  
  10.     {  
  11.         public FrmTimer()  
  12.         {  
  13.             InitializeComponent();  
  14.         }  
  15.         #region "变量定义"  
  16.         private System.Windows.Forms.Timer timer;  
  17.         Random rd;  
  18.         #endregion  
  19.         #region "Timer相关的内容"  
  20.         /// <summary>  
  21.         /// 初始化Timer  
  22.         /// </summary>  
  23.         private void InitTimer()  
  24.         {  
  25.             Console.WriteLine("进入 InitTimer");  
  26.             this.timer = new System.Windows.Forms.Timer();  
  27.             this.timer.Interval = 5000;  
  28.             this.timer.Tick += new System.EventHandler(this.timer1_Tick);  
  29.             this.timer.Start();  
  30.             Console.WriteLine("走出 InitTimer");  
  31.         }  
  32.         /// <summary>  
  33.         /// Timer每隔一定时间将会让画面执行一次这个方法  
  34.         /// </summary>  
  35.         /// <param name="sender"></param>  
  36.         /// <param name="e"></param>  
  37.         private void timer1_Tick(object sender, EventArgs e)  
  38.         {  
  39.             Console.WriteLine("进入 timer1_Tick");  
  40.             this.UseWaitCursor = true;  
  41.             // 假设这个时候有比较耽误时间的操作  
  42.             // 此处我用Sleep来代替  
  43.             Console.WriteLine("开始忙碌");  
  44.             // 画面将会出现假死现象  
  45.             System.Threading.Thread.Sleep(3000);  
  46.             Console.WriteLine("停止忙碌");  
  47.             foreach (DataGridViewRow row in dataGridView1.Rows)  
  48.             {  
  49.                 if (rd.Next(1, 100) % 2 == 0)  
  50.                 {  
  51.                     row.Cells["columnStatus"].Value = "连接";  
  52.                 }  
  53.                 else  
  54.                 {  
  55.                     row.Cells["columnStatus"].Value = "断开";  
  56.                 }  
  57.             }  
  58.             this.UseWaitCursor = false;  
  59.             Console.WriteLine("走出 timer1_Tick");  
  60.         }  
  61.         #endregion  
  62.         #region "与Form有关的内容"  
  63.         // 定义了一个产生随机数的对象  
  64.         // 目的是为了随机生成画面上的状态  
  65.   
  66.         /// <summary>  
  67.         /// Form的Load事件  
  68.         /// </summary>  
  69.         /// <param name="sender"></param>  
  70.         /// <param name="e"></param>  
  71.         private void FrmTimer_Load(object sender, EventArgs e)  
  72.         {  
  73.             Console.WriteLine("进入 FrmTimer_Load");  
  74.             InitDataGridView();  
  75.             InitTimer();  
  76.             // 给随机数一个种子  
  77.             rd = new Random(DateTime.Now.Millisecond);  
  78.             Console.WriteLine("走出 FrmTimer_Load");  
  79.         }  
  80.         /// <summary>  
  81.         /// 给画面上的DataGridView赋初值  
  82.         /// </summary>  
  83.         private void InitDataGridView()  
  84.         {  
  85.             Console.WriteLine("进入 InitDataGridView");  
  86.             for (Int32 i = 1; i < 6; i++)  
  87.             {  
  88.                 dataGridView1.Rows.Add(new String[] { "192.168.1." + i.ToString(), "断开" });  
  89.             }  
  90.             Console.WriteLine("走出 InitDataGridView");  
  91.         }  
  92.         /// <summary>  
  93.         /// 关闭画面  
  94.         /// </summary>  
  95.         /// <param name="sender"></param>  
  96.         /// <param name="e"></param>  
  97.         private void btnClose_Click(object sender, EventArgs e)  
  98.         {  
  99.             Console.WriteLine("进入 btnClose_Click");  
  100.             this.Close();  
  101.             Console.WriteLine("走出 btnClose_Click");  
  102.         }  
  103.         /// <summary>  
  104.         /// 画面关闭时  
  105.         /// </summary>  
  106.         /// <param name="sender"></param>  
  107.         /// <param name="e"></param>  
  108.         private void FrmTimer_FormClosing(object sender, FormClosingEventArgs e)  
  109.         {  
  110.             Console.WriteLine("进入 FrmTimer_FormClosing");  
  111.             timer.Stop();  
  112.             Console.WriteLine("走出 FrmTimer_FormClosing");  
  113.         }  
  114.         #endregion  
  115.    
  116.     }  
  117. }  
 

 

利用Timer的实现机制实际上是事件(event)机制

 

this.timer.Tick += new System.EventHandler(this.timer1_Tick);

这一行代码是让Timer在指定时间触发一个事件

而画面定义一个方法

private void timer1_Tick(object sender, EventArgs e)

对此事件进行响应。

 

此响应方法是Form的线程去执行的方法,所以,在此方法内执行很耗时的操作时。画面将会出现假死现象。

 

例如:读数据库或者进行网络连接以及复杂运算等等。

本例采用

System.Threading.Thread.Sleep(3000);

来代替耗时操作。实际运行时出现画面没响应的现象,具体现象请下载代码实践一下。

以下是执行时输出的日志

 

[c-sharp]  view plain  copy
  1. 进入 FrmTimer_Load  
  2. 进入 InitDataGridView  
  3. 走出 InitDataGridView  
  4. 进入 InitTimer  
  5. 走出 InitTimer  
  6. 走出 FrmTimer_Load  
  7. 进入 timer1_Tick  
  8. 开始忙碌  
  9. 停止忙碌  
  10. 走出 timer1_Tick  
  11. 进入 timer1_Tick  
  12. 开始忙碌  
  13. 停止忙碌  
  14. 走出 timer1_Tick  
  15. 进入 btnClose_Click  
  16. 进入 FrmTimer_FormClosing  
  17. 走出 FrmTimer_FormClosing  
  18. 走出 btnClose_Click  
 

 

在每次出现 “开始忙碌” 的时候画面开始假死,鼠标点击画面没反应。停止忙碌时恢复。

 

 

我们再来看看BackgroundWorker的表现

 

代码如下

 

[c-sharp] view plain copy
  1. using System;  
  2. using System.ComponentModel;  
  3. using System.Windows.Forms;  
  4. namespace TimerAndWorker  
  5. {  
  6.     /// <summary>  
  7.     /// BackgroundWorker示例  
  8.     /// 运行时不会出现画面假死  
  9.     /// </summary>  
  10.     public partial class FrmWorker : Form  
  11.     {  
  12.         public FrmWorker()  
  13.         {  
  14.             InitializeComponent();  
  15.         }  
  16.         #region "Worker"  
  17.         // 定义一个BackgroundWorker  
  18.         BackgroundWorker worker;  
  19.         /// <summary>  
  20.         /// 初始化worker  
  21.         /// </summary>  
  22.         private void InitWorker()  
  23.         {  
  24.             Console.WriteLine("进入 InitWorker");  
  25.             // 实例化  
  26.             worker = new BackgroundWorker();  
  27.             // 给worker指派他要干的活  
  28.             worker.DoWork += new DoWorkEventHandler(worker_DoWork);  
  29.               
  30.             // 给worker添加回调  
  31.             worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);  
  32.               
  33.             // 让worker支持取消  
  34.             worker.WorkerSupportsCancellation = true;  
  35.             // 让worker去干活  
  36.             worker.RunWorkerAsync();  
  37.             Console.WriteLine("走出 InitWorker");  
  38.         }  
  39.         /// <summary>  
  40.         /// Worker工作完成时的回调函数  
  41.         /// </summary>  
  42.         /// <param name="sender"></param>  
  43.         /// <param name="e"></param>  
  44.         void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)  
  45.         {  
  46.             Console.WriteLine("进入 worker_RunWorkerCompleted");  
  47.             // 当worker结束的时候就是画面停止刷新的时候  
  48.             // 所以worker结束时关闭画面  
  49.             this.Close();  
  50.             Console.WriteLine("走出 worker_RunWorkerCompleted");  
  51.         }  
  52.         /// <summary>  
  53.         /// Worker要干的工作  
  54.         /// </summary>  
  55.         /// <param name="sender"></param>  
  56.         /// <param name="e"></param>  
  57.         void worker_DoWork(object sender, DoWorkEventArgs e)  
  58.         {  
  59.             Console.WriteLine("进入 worker_DoWork");  
  60.             while ((sender as BackgroundWorker).CancellationPending == false)  
  61.             {  
  62.                 // 假设这里是很漫长的工作  
  63.                 Console.WriteLine("开始忙碌");  
  64.                 // 这里并不会出现画面的假死  
  65.                 System.Threading.Thread.Sleep(3000);  
  66.                 Console.WriteLine("停止忙碌");  
  67.                 // 更改画面状态  
  68.                 foreach (DataGridViewRow row in dataGridView1.Rows)  
  69.                 {  
  70.                     if (rd.Next(1, 100) % 2 == 0)  
  71.                     {  
  72.                         row.Cells["columnStatus"].Value = "连接";  
  73.                     }  
  74.                     else  
  75.                     {  
  76.                         row.Cells["columnStatus"].Value = "断开";  
  77.                     }  
  78.                 }  
  79.             }  
  80.             Console.WriteLine("走出 worker_DoWork");  
  81.         }  
  82.         #endregion  
  83.         #region "与Form有关的内容"  
  84.         // 定义了一个产生随机数的对象  
  85.         // 目的是为了随机生成画面上的状态  
  86.         Random rd;  
  87.         private void FrmWorker_Load(object sender, EventArgs e)  
  88.         {  
  89.             Console.WriteLine("进入 FrmWorker_Load");  
  90.             InitDataGridView();  
  91.             InitWorker();  
  92.             // 给随机数一个种子  
  93.             rd = new Random(DateTime.Now.Millisecond);  
  94.             Console.WriteLine("走出 FrmWorker_Load");  
  95.         }  
  96.         /// <summary>  
  97.         /// 给画面上的DataGridView赋初值  
  98.         /// </summary>  
  99.         private void InitDataGridView()  
  100.         {  
  101.             Console.WriteLine("进入 InitDataGridView");  
  102.             for (Int32 i = 1; i < 6; i++)  
  103.             {  
  104.                 dataGridView1.Rows.Add(new String[] { "192.168.1." + i.ToString(), "断开" });  
  105.             }  
  106.             Console.WriteLine("走出 InitDataGridView");  
  107.         }  
  108.         /// <summary>  
  109.         /// 点击关闭按钮时候  
  110.         /// </summary>  
  111.         /// <param name="sender"></param>  
  112.         /// <param name="e"></param>  
  113.         private void btnClose_Click(object sender, EventArgs e)  
  114.         {  
  115.             Console.WriteLine("进入 btnClose_Click");  
  116.             // 让worker取消当前任务  
  117.             // worker结束时就会关画面  
  118.             worker.CancelAsync();  
  119.             this.UseWaitCursor = true;  
  120.             this.Visible = false;  
  121.             Console.WriteLine("走出 btnClose_Click");  
  122.         }  
  123.         /// <summary>  
  124.         /// 画面关闭时  
  125.         /// </summary>  
  126.         /// <param name="sender"></param>  
  127.         /// <param name="e"></param>  
  128.         private void FrmWorker_FormClosing(object sender, FormClosingEventArgs e)  
  129.         {  
  130.             Console.WriteLine("进入 FrmWorker_FormClosing");  
  131.             if (worker.IsBusy)  
  132.             {  
  133.                 // 如果worker正在忙则应该取消本次关闭画面的动作  
  134.                 e.Cancel = true;  
  135.                 // 等待worker关闭当前画面  
  136.                 worker.CancelAsync();  
  137.                 this.Visible = false;                  
  138.             }  
  139.             Console.WriteLine("走出 FrmWorker_FormClosing");  
  140.         }  
  141.         #endregion  
  142.     }  
  143. }  

 

由于backgroundworker是后台线程,所以不会出现画面假死现象。即便是

耗时操作

System.Threading.Thread.Sleep(3000);

也不会假死

所以。我建议如果有类似功能要实现的朋友要优先选择BackgroundWorker

这样可以让你的程序界面更灵活。

 

 

 

顺便提一句

====================================================================

以下的文字全是个人理解,很有可能与事实有偏差,欢迎指正。

====================================================================

如果说Form是一个线程,BackgroundWorker是另外一个线程。Form中的控件有可能会被禁止在BackgroundWorker的

Dowork中调用,这种调用被称为跨线程调用。如果出现这样的问题。请尝试下面的方法。

 

DataGridView.CheckForIllegalCrossThreadCalls = true;

 

DataGridView这里用的是类名而不是对象的引用

 

CheckForIllegalCrossThreadCalls 是 Control 类的成员

 

 

Control. CheckForIllegalCrossThreadCalls

MSDN上的说明是:获取或设置一个值,该值指示是否捕获对错误线程的调用,这些调用在调试应用程序时访问控件的 Handle 属性。

 

 

改变CheckForIllegalCrossThreadCalls 的值,则可以让控件可以被跨线程操作。

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值