首页 > Programming > 通用的异步处理类和进度通知类及其示例

通用的异步处理类和进度通知类及其示例

2008年5月17日 Galaxy 发表评论 阅读评论

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: , ,

Related posts

分类: Programming 标签: , , 171 views
1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.

Locations of visitors to this page