用Timer还是用BackgroundWorker实现定时更新画面的功能
2010-02-10 17:47
309 查看
第一次发文章,水平有限,如有错误欢迎大家指出。
声明:
1,测试环境为vs2008, .net framework 3.5
2,此次我使用的Timer是 System.Windows.Forms 命名空间下的 Timer 组件,另外两个Timer我没试验。
3,我没有用工具箱添加Timer和BackgroundWorker
首先
介绍一下画面
总共有3个Form
点击第一个图标弹出下面的画面
点击第二个图标弹出下面的画面
两个画面很类似。
实现的是同一个功能。
主画面功能不详述。
先说第一个画面
利用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);
来代替耗时操作。实际运行时出现画面没响应的现象,具体现象请下载代码实践一下。
以下是执行时输出的日志
在每次出现 “开始忙碌” 的时候画面开始假死,鼠标点击画面没反应。停止忙碌时恢复。
我们再来看看BackgroundWorker的表现
代码如下
由于backgroundworker是后台线程,所以不会出现画面假死现象。即便是
耗时操作
System.Threading.Thread.Sleep(3000);
也不会假死
所以。我建议如果有类似功能要实现的朋友要优先选择BackgroundWorker
这样可以让你的程序界面更灵活。
顺便提一句
====================================================================
以下的文字全是个人理解,很有可能与事实有偏差,欢迎指正。
====================================================================
如果说Form是一个线程,BackgroundWorker是另外一个线程。Form中的控件有可能会被禁止在BackgroundWorker的
Dowork中调用,这种调用被称为跨线程调用。如果出现这样的问题。请尝试下面的方法。
DataGridView.CheckForIllegalCrossThreadCalls = true;
DataGridView这里用的是类名而不是对象的引用
CheckForIllegalCrossThreadCalls 是 Control 类的成员
Control. CheckForIllegalCrossThreadCalls
MSDN上的说明是:获取或设置一个值,该值指示是否捕获对错误线程的调用,这些调用在调试应用程序时访问控件的 Handle 属性。
改变CheckForIllegalCrossThreadCalls 的值,则可以让控件可以被跨线程操作。
2010年2月7日
秋叶1片
声明:
1,测试环境为vs2008, .net framework 3.5
2,此次我使用的Timer是 System.Windows.Forms 命名空间下的 Timer 组件,另外两个Timer我没试验。
3,我没有用工具箱添加Timer和BackgroundWorker
首先
介绍一下画面
总共有3个Form
点击第一个图标弹出下面的画面
点击第二个图标弹出下面的画面
两个画面很类似。
实现的是同一个功能。
主画面功能不详述。
先说第一个画面
using System; using System.Windows.Forms; namespace TimerAndWorker { /// <summary> /// System.Windows.Forms.Timer示例 /// 运行时会出现画面假死 /// </summary> public partial class FrmTimer : Form { public FrmTimer() { InitializeComponent(); } #region "变量定义" private System.Windows.Forms.Timer timer; Random rd; #endregion #region "Timer相关的内容" /// <summary> /// 初始化Timer /// </summary> private void InitTimer() { Console.WriteLine("进入 InitTimer"); this.timer = new System.Windows.Forms.Timer(); this.timer.Interval = 5000; this.timer.Tick += new System.EventHandler(this.timer1_Tick); this.timer.Start(); Console.WriteLine("走出 InitTimer"); } /// <summary> /// Timer每隔一定时间将会让画面执行一次这个方法 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void timer1_Tick(object sender, EventArgs e) { Console.WriteLine("进入 timer1_Tick"); this.UseWaitCursor = true; // 假设这个时候有比较耽误时间的操作 // 此处我用Sleep来代替 Console.WriteLine("开始忙碌"); // 画面将会出现假死现象 System.Threading.Thread.Sleep(3000); Console.WriteLine("停止忙碌"); foreach (DataGridViewRow row in dataGridView1.Rows) { if (rd.Next(1, 100) % 2 == 0) { row.Cells["columnStatus"].Value = "连接"; } else { row.Cells["columnStatus"].Value = "断开"; } } this.UseWaitCursor = false; Console.WriteLine("走出 timer1_Tick"); } #endregion #region "与Form有关的内容" // 定义了一个产生随机数的对象 // 目的是为了随机生成画面上的状态 /// <summary> /// Form的Load事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void FrmTimer_Load(object sender, EventArgs e) { Console.WriteLine("进入 FrmTimer_Load"); InitDataGridView(); InitTimer(); // 给随机数一个种子 rd = new Random(DateTime.Now.Millisecond); Console.WriteLine("走出 FrmTimer_Load"); } /// <summary> /// 给画面上的DataGridView赋初值 /// </summary> private void InitDataGridView() { Console.WriteLine("进入 InitDataGridView"); for (Int32 i = 1; i < 6; i++) { dataGridView1.Rows.Add(new String[] { "192.168.1." + i.ToString(), "断开" }); } Console.WriteLine("走出 InitDataGridView"); } /// <summary> /// 关闭画面 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnClose_Click(object sender, EventArgs e) { Console.WriteLine("进入 btnClose_Click"); this.Close(); Console.WriteLine("走出 btnClose_Click"); } /// <summary> /// 画面关闭时 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void FrmTimer_FormClosing(object sender, FormClosingEventArgs e) { Console.WriteLine("进入 FrmTimer_FormClosing"); timer.Stop(); Console.WriteLine("走出 FrmTimer_FormClosing"); } #endregion } }
利用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);
来代替耗时操作。实际运行时出现画面没响应的现象,具体现象请下载代码实践一下。
以下是执行时输出的日志
进入 FrmTimer_Load 进入 InitDataGridView 走出 InitDataGridView 进入 InitTimer 走出 InitTimer 走出 FrmTimer_Load 进入 timer1_Tick 开始忙碌 停止忙碌 走出 timer1_Tick 进入 timer1_Tick 开始忙碌 停止忙碌 走出 timer1_Tick 进入 btnClose_Click 进入 FrmTimer_FormClosing 走出 FrmTimer_FormClosing 走出 btnClose_Click
在每次出现 “开始忙碌” 的时候画面开始假死,鼠标点击画面没反应。停止忙碌时恢复。
我们再来看看BackgroundWorker的表现
代码如下
using System; using System.ComponentModel; using System.Windows.Forms; namespace TimerAndWorker { /// <summary> /// BackgroundWorker示例 /// 运行时不会出现画面假死 /// </summary> public partial class FrmWorker : Form { public FrmWorker() { InitializeComponent(); } #region "Worker" // 定义一个BackgroundWorker BackgroundWorker worker; /// <summary> /// 初始化worker /// </summary> private void InitWorker() { Console.WriteLine("进入 InitWorker"); // 实例化 worker = new BackgroundWorker(); // 给worker指派他要干的活 worker.DoWork += new DoWorkEventHandler(worker_DoWork); // 给worker添加回调 worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); // 让worker支持取消 worker.WorkerSupportsCancellation = true; // 让worker去干活 worker.RunWorkerAsync(); Console.WriteLine("走出 InitWorker"); } /// <summary> /// Worker工作完成时的回调函数 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { Console.WriteLine("进入 worker_RunWorkerCompleted"); // 当worker结束的时候就是画面停止刷新的时候 // 所以worker结束时关闭画面 this.Close(); Console.WriteLine("走出 worker_RunWorkerCompleted"); } /// <summary> /// Worker要干的工作 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void worker_DoWork(object sender, DoWorkEventArgs e) { Console.WriteLine("进入 worker_DoWork"); while ((sender as BackgroundWorker).CancellationPending == false) { // 假设这里是很漫长的工作 Console.WriteLine("开始忙碌"); // 这里并不会出现画面的假死 System.Threading.Thread.Sleep(3000); Console.WriteLine("停止忙碌"); // 更改画面状态 foreach (DataGridViewRow row in dataGridView1.Rows) { if (rd.Next(1, 100) % 2 == 0) { row.Cells["columnStatus"].Value = "连接"; } else { row.Cells["columnStatus"].Value = "断开"; } } } Console.WriteLine("走出 worker_DoWork"); } #endregion #region "与Form有关的内容" // 定义了一个产生随机数的对象 // 目的是为了随机生成画面上的状态 Random rd; private void FrmWorker_Load(object sender, EventArgs e) { Console.WriteLine("进入 FrmWorker_Load"); InitDataGridView(); InitWorker(); // 给随机数一个种子 rd = new Random(DateTime.Now.Millisecond); Console.WriteLine("走出 FrmWorker_Load"); } /// <summary> /// 给画面上的DataGridView赋初值 /// </summary> private void InitDataGridView() { Console.WriteLine("进入 InitDataGridView"); for (Int32 i = 1; i < 6; i++) { dataGridView1.Rows.Add(new String[] { "192.168.1." + i.ToString(), "断开" }); } Console.WriteLine("走出 InitDataGridView"); } /// <summary> /// 点击关闭按钮时候 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnClose_Click(object sender, EventArgs e) { Console.WriteLine("进入 btnClose_Click"); // 让worker取消当前任务 // worker结束时就会关画面 worker.CancelAsync(); this.UseWaitCursor = true; this.Visible = false; Console.WriteLine("走出 btnClose_Click"); } /// <summary> /// 画面关闭时 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void FrmWorker_FormClosing(object sender, FormClosingEventArgs e) { Console.WriteLine("进入 FrmWorker_FormClosing"); if (worker.IsBusy) { // 如果worker正在忙则应该取消本次关闭画面的动作 e.Cancel = true; // 等待worker关闭当前画面 worker.CancelAsync(); this.Visible = false; } Console.WriteLine("走出 FrmWorker_FormClosing"); } #endregion } }
由于backgroundworker是后台线程,所以不会出现画面假死现象。即便是
耗时操作
System.Threading.Thread.Sleep(3000);
也不会假死
所以。我建议如果有类似功能要实现的朋友要优先选择BackgroundWorker
这样可以让你的程序界面更灵活。
顺便提一句
====================================================================
以下的文字全是个人理解,很有可能与事实有偏差,欢迎指正。
====================================================================
如果说Form是一个线程,BackgroundWorker是另外一个线程。Form中的控件有可能会被禁止在BackgroundWorker的
Dowork中调用,这种调用被称为跨线程调用。如果出现这样的问题。请尝试下面的方法。
DataGridView.CheckForIllegalCrossThreadCalls = true;
DataGridView这里用的是类名而不是对象的引用
CheckForIllegalCrossThreadCalls 是 Control 类的成员
Control. CheckForIllegalCrossThreadCalls
MSDN上的说明是:获取或设置一个值,该值指示是否捕获对错误线程的调用,这些调用在调试应用程序时访问控件的 Handle 属性。
改变CheckForIllegalCrossThreadCalls 的值,则可以让控件可以被跨线程操作。
2010年2月7日
秋叶1片
相关文章推荐
- 用Timer还是用BackgroundWorker实现定时更新画面的功能
- Java:利用java Timer类实现定时执行任务的功能
- Java定时任务:利用java Timer类实现定时执行任务的功能
- android AppWidget的使用以及利用TimerTask实现widget的定时更新
- 借助JavaEE中Timer API实现定时关闭计算机的功能
- handler实现自定义对话框(Dialog)的数据定时(TimerTask)更新
- Java____Timer实现定时功能及其源码研究
- handler实现自定义对话框(Dialog)的数据定时(TimerTask)更新
- Java:利用java Timer类实现定时执行任务的功能
- SetWaitableTimer 通过异步程序调用(APC)实现的定时功能
- 通过异步程序调用(APC)实现的定时功能 CreateWaitableTimer和SetWaitableTimer函数
- handler实现自定义对话框(Dialog)的数据定时(TimerTask)更新
- android AppWidget的使用以及利用TimerTask实现widget的定时更新
- 利用PHP安装windows自动运行的服务,PHP程序可以实现长时间、自动运行、定时更新功能,直接可以用在项目中的类源代码
- java Timer(定时调用、实现固定时间执行)
- 自动更新功能简单实现
- Python通过调用mysql存储过程实现更新数据功能示例
- 用java界面实现根据当前系统时间定时更新二维码
- 页面即时更新金额功能的实现
- Android应用自动更新功能的代码实现