通用的异步处理类和进度通知类及其示例
http://www.cnblogs.com/BigTall/archive/2005/03/09/115585.html
通用的异步处理类和进度通知类及其示例
在上文《我的界面进度条异步显示模式》中,我提到了使用异步处理显示进度条的时候,我觉得用起来比较顺手的一种组织代码的方法,相比起来,这种方法的最大特点就是把代码尽量地从界面层剥离,并且让界面之间的关联尽可能少些。
我在处理过程中使用了一个封装了异步线程处理的一个抽象类,这个抽象类实现了异步处理的 Start,Abort, Pause 和 Resume 接口,大家用它来实现异步处理的时候,就可以象玩秒表一样随意地控制这些处理过程了。完整的代码如下:
public abstract class AsyncWorker
{
public enum WorkState{Ready, Working, Pause,Resume,Aborted, Done, Error}
private WorkState m_State;
private bool m_Wait;
private Thread m_Thread;
protected AsyncWorker(bool wait)
{
m_State = WorkState.Ready;
m_Wait = wait;
m_Thread = new Thread(new ThreadStart(Work));
}
public void Start()
{
OnStateChangedSync(WorkState.Ready);
m_Thread.Start();
while( m_Wait && m_Thread.IsAlive )
{
OnIdle();
Thread.Sleep(200);
}
}
public void Pause()
{
if( m_State == WorkState.Working)
{
m_Thread.Suspend();
m_State = WorkState.Pause;
OnStateChanged(WorkState.Pause);
}
}
public void Resume()
{
if( m_State == WorkState.Pause )
{
m_State = WorkState.Working;
m_Thread.Resume();
OnStateChanged(WorkState.Resume);
}
}
public void Abort()
{
if( m_State == WorkState.Working || m_State == WorkState.Pause )
{
m_Thread.Abort();
m_State = WorkState.Aborted;
OnStateChanged(WorkState.Aborted);
}
}
private void Work()
{
try
{
m_State = WorkState.Working;
OnStateChangedSync(WorkState.Working);
DoWork();
m_State = WorkState.Done;
OnStateChanged(WorkState.Done);
}
catch (Exception e)
{
m_State = WorkState.Error;
OnError(e);
OnStateChanged(WorkState.Error);
}
}
protected abstract void DoWork();
private void OnStateChangedSync(WorkState state)
{
if( StateChanged != null )
StateChanged(this, state);
}
private void OnStateChanged(WorkState state)
{
if( StateChanged != null )
StateChanged.BeginInvoke(this, state, null, null);
}
protected void OnIdle()
{
if( Idle != null )
{
lock(this) // 有可能会很高频率调用
Idle(this);
}
}
protected void OnError(Exception e)
{
if( Error != null )
Error.BeginInvoke(this, e, null, null);
}
public delegate void StateChangedEventHandler(AsyncWorker sender, WorkState state);
public delegate void IdleEventHandler(AsyncWorker sender);
public delegate void ErrorEventHandler(AsyncWorker sender, Exception e);
public event StateChangedEventHandler StateChanged;
public event IdleEventHandler Idle;
public event ErrorEventHandler Error;
}
在处理进度变化的时候,我使用了一个抽象的接口 IProgress,这个抽象接口提供外部对进度的查询功能,接口定义如下:
/**//// <summary>
/// 反映进度的变化的接口
/// </summary>
public interface IProgress
{
int Total{get;}
int CurrentProgress{get;}
string Description{get;}
}
在具体实现的时候,可以看我的 ProgressMoniter 的代码,这个是利用一个单独的线程来定期轮询 IProgress 接口进度变化,然后产生事件通知,是最好的说明例子,如果有人想修改成非轮询的方式,也很容易。
public class ProgressMoniter : AsyncWorker
{
public delegate void ProgressEventHandler(string text, int percent);
public event ProgressEventHandler Progress;
IProgress m_Progress;
private bool m_Exit;
public bool Exit { get { return m_Exit; } set { m_Exit = value; } }
public ProgressMoniter(IProgress progress) : base(false)
{
m_Progress = progress;
m_Exit = false;
}
protected override void DoWork()
{
while( !m_Exit )
{
lock(m_Progress)
OnProgress(m_Progress.Description, m_Progress.Total, m_Progress.CurrentProgress);
Thread.Sleep(200);
}
}
private void OnProgress(string description, int total, int progress)
{
if( Progress != null )
Progress.BeginInvoke(description, (int)((long)progress * 100 / (long)total), null, null);
}
}
http://www.cnblogs.com/BigTall/archive/2005/03/09/115571.html
我的界面进度条异步显示模式
系统的注册表坏了,又不想重装,于是考虑编一个程序来比较一下注册表的变化(我每装一个重要软件都会备份注册表)。在实际编程的时候,因为处理注册表的时间比较长,需要显示一个进度条,而且要异步处理,避免界面长时间没有响应的问题。
原来的处理方法采用过如下的两种(以下是伪代码):
class Form1
{
void OnStartButtonClick()
{
ProgressForm prog = new ProgressForm();
// 预处理
prog.ShowDialog();
// 其他处理
}
}
class ProgressForm
{
private Timer m_Timer; //设置时钟, 100ms
void TimerTick() // 时钟触发
{
m_Timer.Enable = false; // 时钟只需要用一次
// 以下开始正式的处理过程
WorkThread th = new WorkThread();
th.ProgressEvent += new ProgressEvent(ProgressCallback);
//
}
void ProgressCallback(){ }
}
另外一种是觉得使用时钟不好,绕来绕去太麻烦,又想把调用 WorkThread 部分代码放到 Form1 中,但是如此一来,ShowDialog 就不能用了,代码变成了如下:
class Form1
{
ProgressForm prog;
void OnStartButtonClick()
{
prog = new ProgressForm();
// 预处理
this.Enabled = false; // 禁止自身,保证 ProgressForm 在窗口前面
prog.Show();
// 开始处理
WorkThread th = new WorkThread();
th.ProgressEvent += new ProgressEvent(ProgressCallback);
// 其他处理
}
void ProgressCallback()
{
prog.DisplayProgress(total, percent, text);
}
}
class ProgressForm
{
public void DisplayProgress( )
{
// 刷新进度条
}
}
但是使用过程中发现窗口这样的关系就变得很尴尬,而且,就这样的代码,我始终无法把ProgressForm中的 “中断操作” 按钮的代码找到一个满意的实现方法。
最后,经过重新的考虑和设计,最终的代码把 WorkThread 设计了一个通用的接口,屏蔽了使用线程的一些繁琐,并且提供了 Start, Pause, Resume,Abort 接口,并通过事件发送对外通知。另外,取消频繁调用进度通知的代码,提供了一个进度查询接口 IProgress,由专门的进度监控线程ProgressMoniter 来监视进度并通知ProgressForm刷新界面。最后的外部代码如下:
class Form1
{
void OnStartButtonClick()
{
ProgressForm prog = new ProgressForm();
// 预处理
// 生成工作线程
WorkThread m_Worker = new WorkThread();
m_Worker.Idle += new IdleEventHandler(m_Worker_Idle);
m_Worker.Error += new ErrorEventHandler(m_Worker_Error);
m_Worker.StateChanged += new StateChangedEventHandler(m_Worker_StateChanged);
// 生成监控线程
ProgressMoniter m_Moniter = new ProgressMoniter(m_Worker);
prog.Assign(m_Moniter); // ProgressForm 关联监控线程的进度变化事件
m_Worker.Start(); // 开始工作
m_Moniter.Start(); // 开始监控进度
prog.ShowDialog(); // ShowDialog显示,这下不用 this.Enable = false 了
// ShowDialog会一直等待工作完成之后才返回,然后作其他处理
}
}
class ProgressForm
{
public void Assign(ProgressMoniter moniter)
{
DisplayProgress(“ready”, 0);
this.btnCancel.Enabled = true; // 允许中断
m_Worker = moniter;
moniter.Progress += new ProgressEventHandler(DisplayProgress); // 挂接事件
}
public void DisplayProgress( )
{
// 刷新进度条
}
}
其他部分的代码和实现在下一篇文章里,等一下我就写。
请看《通用的异步处理类和进度通知类及其示例》。
Tags: C/C++, Programming, ZT
MoeFan (萌番)
最近评论