您的位置:首页 > 其它

用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



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



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



两个画面很类似。

实现的是同一个功能。

主画面功能不详述。

先说第一个画面

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片
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: