思想决定人生,态度改变一切

成功者找方法,失败者找借口! 做事先做人;安分做人,本分做事!

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
一、引言 
     在编写Windows form时,如果直接在UI线程要运行一个费时方法的话(如从数据库查询大量数据时),会引起程序“假死”,从而导致用户不满。这个时候就需要通过多线程技术来解决,提高界面交互性能,方便用户使用。 

一般通过三种方式解决: 

1.通过System.Threading.Thread类,创建新的线程,Thread.Start运行费时方法。 
2.通过System.Threading.ThreadPool类,将费时任务提交到线程池中,等待运行。 
以上两种方法,基本思路是在UI界面中控制线程的启动和中止,在线程中回调用UI界面方法,更新界面。在线程中回调UI界面方法时,特别是涉及更新控件属性时,如果不注意,存在很大的隐患。这两种办法,编码和控制结构较为复杂,需要启动和管理额外的线程占用资源。 
3.通过异步委托调用,将该方法排队到系统线程池的线程中运行,而在费时方法中也通过Control.BeginInvoke异步回调,达到"启动后不管"的目的。 
这种方法,编码简单,程序结构较为清晰,充分利用.NET框架的异步委托功能,但要对异步调用知识较熟悉。 

相关知识点参见 
http://net66.cnblogs.com/net66/admin/archive/2005/08/02/206067.html
     现利用.NET异步委托调用功能,编写Task抽象类,以方便管理后台工作线程,衔接后台线程与UI线程的联系。该抽象类提供了调用和管理的框架,没有方法的实现细节,通过继承类、重写方法,
可以实现想要的功能。主要功能如下: 

1.利用异步委托调用,实际多线程,不需要单独后台线程。 
2.通过委托、事件驱动,实际后台与前台UI线程的联系,实现事件广播。 
3.支持正常取消后台工作方法(费时方法)运行,也可以强制中止线程。 
4.能够捕获取消、强制中止和方法出错三种情况,并突发相关事件,以便进行释放资源等操作。 
5.通过异步调用,在工作方法中安全调用涉及UI控件的方法。 
6.自行管理工作进程状态,提供状态变化事件。 
7.只要工作方法调用签名,符合定义的TaskDelegate委托接口,可通过StartTask(TaskDelegate worker ,params object[] args )方便调用。在实际使用时,可在继承类中定义多个相同调用接口的方法,避免重复编码,较为方便。 

给大家作个参考,而大牛呢,多点指正。当是扔个砖头,想砸块玉吧。

二、代码
复制  保存
using System;
using System.Windows.Forms;
namespace Net66.AsynchThread
{
/// <summary>
    /// 任务工作状态
    /// </summary>
    public enum TaskStatus
{
/// <summary>
        /// 任务没有运行,可能是工作进程没有开始、工作进程正常结束或正常取消工作进程
        /// </summary>
        Stopped,
/// <summary>
        /// 任务没有运行,被调用者强行中止
        /// </summary>
        Aborted,
/// <summary>
        /// 任务没有运行,在工作进程中触发错误而中止
        /// </summary>
        ThrowErrorStoped,
/// <summary>
        /// 任务运行中
        /// </summary>
        Running,
/// <summary>
        /// 尝试取消工作进程中
        /// </summary>
        CancelPending,
/// <summary>
        /// 强行中止工作进程中
        /// </summary>
        AbortPending
}
/// <summary>
    /// 任务状态消息
    /// </summary>
    public class TaskEventArgs : EventArgs
{
/// <summary>
        /// 任务运行结果
        /// </summary>
        public Object Result;
/// <summary>
        /// 任务进度(0-100)
        /// </summary>
        public int Progress;
/// <summary>
        /// 任务工作状态
        /// </summary>
        public TaskStatus Status;
/// <summary>
        /// 任务消息文本
        /// </summary>
        public String Message;
/// <summary>
        /// 创建任务状态消息
        /// </summary>
        /// <param name="progress">任务进度(0-100)</param>
        public TaskEventArgs(int progress)
{
this.Progress = progress;
this.Status = TaskStatus.Running;
}
/// <summary>
        /// 创建任务状态消息
        /// </summary>
        /// <param name="status">任务线程状态</param>
        public TaskEventArgs(TaskStatus status)
{
this.Status = status;
}
/// <summary>
        /// 创建任务状态消息
        /// </summary>
        /// <param name="progress">任务进度(0-100)</param>
        /// <param name="result">任务运行中间结果</param>
        public TaskEventArgs(int progress, object result)
{
this.Progress = progress;
this.Status = TaskStatus.Running;
this.Result = result;
}
/// <summary>
        /// 创建任务状态消息
        /// </summary>
        /// <param name="status">任务线程状态</param>
        /// <param name="result">任务运行结果</param>
        public TaskEventArgs(TaskStatus status, object result)
{
this.Status = status;
this.Result = result;
}
/// <summary>
        /// 创建任务状态消息
        /// </summary>
        /// <param name="status">任务线程状态</param>
        /// <param name="message">消息文本</param>
        /// <param name="result">任务运行结果</param>
        public TaskEventArgs(TaskStatus status, string message, object result)
{
this.Status = status;
this.Message = message;
this.Result = result;
}
/// <summary>
        /// 创建任务状态消息
        /// </summary>
        /// <param name="progress">任务进度(0-100)</param>
        /// <param name="message">消息文本</param>
        /// <param name="result">任务运行中间结果</param>
        public TaskEventArgs(int progress, string message, object result)
{
this.Progress = progress;
this.Status = TaskStatus.Running;
this.Message = message;
this.Result = result;
}
/// <summary>
        /// 创建任务状态消息
        /// </summary>
        /// <param name="status">任务线程状态</param>
        /// <param name="progress">任务进度(0-100)</param>
        /// <param name="message">消息文本</param>
        /// <param name="result">任务运行中间结果</param>
        public TaskEventArgs(TaskStatus status, int progress, string message, object result)
{
this.Status = status;
this.Progress = progress;
this.Message = message;
this.Result = result;
}
}
/// <summary>
    /// 任务的工作方法(Work)的委托接口
    /// 传入值:对象数组(object[])
    /// 返回值:对象(object)
    /// </summary>
    public delegate object TaskDelegate(params object[] args);
/// <summary>
    /// 任务事件的委托接口
    /// </summary>
    public delegate void TaskEventHandler(object sender, TaskEventArgs e);
abstract public class Task
{
        #region 内部属性
        /// <summary>
        /// 任务调用线程(前台或UI线程)
        /// </summary>
        protected System.Threading.Thread _callThread = null;
/// <summary>
        /// 任务工作线程(后台)
        /// </summary>
        protected System.Threading.Thread _workThread = null;
/// <summary>
        /// 任务工作状态
        /// </summary>
        protected TaskStatus _taskState = TaskStatus.Stopped;
/// <summary>
        /// 任务进度(0-100)
        /// </summary>
        protected int _progress = -1;
/// <summary>
        /// 任务工作结果
        /// </summary>
        protected object _result = null;
/// <summary>
        /// 任务工作进程出错时,捕获的异常对象
        /// </summary>
        protected Exception _exception = null;
        #endregion
        #region 事件
        /// <summary>
        /// 任务工作状态变化事件
        /// </summary>
        public event TaskEventHandler TaskStatusChanged;
/// <summary>
        /// 任务进度变化事件
        /// </summary>
        public event TaskEventHandler TaskProgressChanged;
/// <summary>
        /// 任务被调用者强行中止事件
        /// </summary>
        public event TaskEventHandler TaskAbort;
/// <summary>
        /// 任务工作方法执行中触发错误事件
        /// </summary>
        public event TaskEventHandler TaskThrowError;
/// <summary>
        /// 任务被调用者取消事件
        /// </summary>
        public event TaskEventHandler TaskCancel;
        #endregion
        #region 属性

/// <summary>
        /// 任务工作进程出错时,捕获的异常对象
        /// </summary>
        public Exception Exception
{
get { return _exception; }
}
/// <summary>
        /// 任务调用线程(前台或UI线程)
        /// </summary>
        public System.Threading.Thread CallThread
{
get { return _callThread; }
}
/// <summary>
        /// 任务工作线程(后台)
        /// </summary>
        public System.Threading.Thread WordThread
{
get { return _workThread; }
}
/// <summary>
        /// 任务进度(0-100)
        /// </summary>
        public int Progress
{
get { return _progress; }
}
/// <summary>
        /// 任务工作状态
        /// </summary>
        public TaskStatus TaskState
{
get { return _taskState; }
}
/// <summary>
        /// 任务工作结果
        /// </summary>
        public object Result
{
get { return _result; }
}
protected bool IsStop
{
get
{
bool result = false;
switch (_taskState)
{
case TaskStatus.Stopped:
case TaskStatus.Aborted:
case TaskStatus.ThrowErrorStoped:
result = true;
break;
default:
break;
}
return result;
}
}
        #endregion
        #region 触发事件
        /// <summary>
        /// 触发任务工作状态变化事件
        /// </summary>
        /// <param name="status">任务工作状态</param>
        /// <param name="result">任务工作结果对象</param>
        protected void FireStatusChangedEvent(TaskStatus status, object result)
{
if (TaskStatusChanged != null)
{
TaskEventArgs args = new TaskEventArgs(status, result);
AsyncInvoke(TaskStatusChanged, args);
}
}
/// <summary>
        /// 触发任务进度变化事件
        /// </summary>
        /// <param name="progress">任务进度(0-100)</param>
        /// <param name="result">任务工作中间结果对象</param>
        protected void FireProgressChangedEvent(int progress, object result)
{
if (TaskProgressChanged != null)
{
TaskEventArgs args = new TaskEventArgs(progress, result);
AsyncInvoke(TaskProgressChanged, args);
}
}
/// <summary>
        /// 触发工作方法执行中发现错误事件
        /// </summary>
        /// <param name="progress">任务进度(0-100)</param>
        /// <param name="result">任务工作中间结果对象</param>
        protected void FireThrowErrorEvent(int progress, object result)
{
if (TaskThrowError != null)
{
TaskEventArgs args = new TaskEventArgs(progress, result);
AsyncInvoke(TaskThrowError, args);
}
}
/// <summary>
        /// 触发被调用者取消事件
        /// </summary>
        /// <param name="progress">任务进度(0-100)</param>
        /// <param name="result">任务工作中间结果对象</param>
        protected void FireCancelEvent(int progress, object result)
{
if (TaskCancel != null)
{
TaskEventArgs args = new TaskEventArgs(progress, result);
AsyncInvoke(TaskCancel, args);
}
}
/// <summary>
        /// 触发被调用者强行中止事件
        /// </summary>
        /// <param name="progress">任务进度(0-100)</param>
        /// <param name="result">任务工作中间结果对象</param>
        protected void FireAbortEvent(int progress, object result)
{
if (TaskAbort != null)
{
TaskEventArgs args = new TaskEventArgs(progress, result);
AsyncInvoke(TaskAbort, args);
}
}
/// <summary>
        /// 异步调用挂接事件委托
        /// </summary>
        /// <param name="eventhandler">事件处理方法句柄</param>
        /// <param name="args">事件消息</param>
        protected void AsyncInvoke(TaskEventHandler eventhandler, TaskEventArgs args)
{
//            TaskEventHandler[] tpcs = (TaskEventHandler[])eventhandler.GetInvocationList();
            Delegate[] tpcs = eventhandler.GetInvocationList();
foreach (TaskEventHandler tpc in tpcs)
{
if (tpc.Target is System.Windows.Forms.Control)
{
Control targetForm = tpc.Target as System.Windows.Forms.Control;
targetForm.BeginInvoke(tpc, new object[] { this, args });
}
else
{
tpc.BeginInvoke(this, args, null, null); //异步调用,启动后不管
                }
}
}
        #endregion
        #region 工作进程管理
        /// <summary>
        /// 开启任务默认的工作进程
        /// [public object Work(params  object[] args )]
        /// </summary>
        /// <param name="args">传入的参数数组</param>
        public bool StartTask(params object[] args)
{
return StartTask(new TaskDelegate(Work), args);
}
/// <summary>
        /// 开启任务的工作进程
        /// 将开启符合TaskDelegate委托接口的worker工作方法
        /// </summary>
        /// <param name="worker">工作方法</param>
        /// <param name="args">传入的参数数组</param>
        public bool StartTask(TaskDelegate worker, params object[] args)
{
bool result = false;
lock (this)
{
if (IsStop && worker != null)
{
_result = null;
_callThread = System.Threading.Thread.CurrentThread;
// 开始工作方法进程,异步开启,传送回调方法
                    worker.BeginInvoke(args, new AsyncCallback(EndWorkBack), worker);
// 更新任务工作状态
                    _taskState = TaskStatus.Running;
// 触发任务工作状态变化事件
                    FireStatusChangedEvent(_taskState, null);
result = true;
}
}
return result;
}
/// <summary>
        /// 请求停止任务进程
        /// 是否停止成功,应看任务工作状态属性TaskState是否为TaskStatus.Stop
        /// </summary>
        public bool StopTask()
{
bool result = false;
lock (this)
{
if (_taskState == TaskStatus.Running)
{
// 更新任务工作状态
                    _taskState = TaskStatus.CancelPending;
// 触发任务工作状态变化事件
                    FireStatusChangedEvent(_taskState, _result);
result = true;
}
}
return result;
}
/// <summary>
        /// 强行中止任务的工作线程
        ///
        /// </summary>
        public bool AbortTask()
{
bool result = false;
lock (this)
{
if (_taskState == TaskStatus.Running && _workThread != null)
{
if (_workThread.ThreadState != System.Threading.ThreadState.Stopped)
{
_workThread.Abort();
}
System.Threading.Thread.Sleep(2);
if (_workThread.ThreadState == System.Threading.ThreadState.Stopped)
{
// 更新任务工作状态
                        _taskState = TaskStatus.Aborted;
result = true;
}
else
{
// 更新任务工作状态
                        _taskState = TaskStatus.AbortPending;
result = false;
}
// 触发任务工作状态变化事件
                    FireStatusChangedEvent(_taskState, _result);
}
}
return result;
}
/// <summary>
        /// 工作方法完成后的回调方法
        /// 将检查是否出错,并获取、更新返回结果值
        /// </summary>
        /// <param name="ar">异步调用信号对象</param>
        protected void EndWorkBack(IAsyncResult ar)
{
bool error = false;
bool abort = false;
try                                                //检查是否错误
{
TaskDelegate del = (TaskDelegate) ar.AsyncState;
_result = del.EndInvoke(ar);
}
catch (Exception e)                                //如果错误,则保存错误对象
            {
error = true;
_exception = e;
if (e.GetType() == typeof(System.Threading.ThreadAbortException))
{
abort = true;
FireAbortEvent(_progress, _exception);
}
else
{
FireThrowErrorEvent(_progress, _exception);
}
}
lock (this)
{
if (error)
{
if (abort)
{
_taskState = TaskStatus.Aborted;        //调用者强行中止
                    }
else
{
_taskState = TaskStatus.ThrowErrorStoped;//出现错误而中止
                    }
}
else
{ _taskState = TaskStatus.Stopped; }          //正常结束
                FireStatusChangedEvent(_taskState, _result);
}
}
        #endregion
        #region 工作方法的基础
        /// <summary>
        /// 工作方法
        /// 在继承类中应重写(override)此方法,以实现具体的工作内容,注意以几点:
        /// 1.须在继承类是引用base.Work,在基类(base)的Work方法中,执行线程设为IsBackground=true,并保存工作线程对象
        /// 2.在继承类中,应及时更新_progress与_result对象,以使Progress和Result属性值正确
        /// 3.在执行过程中应检查_taskState,以使任务中被请求停止后(_taskState为TaskStatus.CancelPending),工作线程能最快终止.
        /// 4.如在继承类中新定义了事件,应在此方法中引用触发
        /// 5.工作线程状态不由工作方法管理,所以在工作方法中不应改变_taskState变量值
        /// 6.工作方法中应对args参数进行有效检查
        /// </summary>
        /// <param name="args">传入的参数数组</param>
        /// <returns>返回null</returns>
        virtual public object Work(params  object[] args)
{
System.Threading.Thread.CurrentThread.IsBackground = true;
_workThread = System.Threading.Thread.CurrentThread;
_result = null;
return null;
}

#endregion
    }
}

  

使用 Task 类 
 
一.在UI线程中创建Task类 
 
Task 类负责管理后台线程。要使用 Task 类,必须做的事情就是创建一个 Task 对象,注册它激发的事件,并且实现这些事件的处理。因为事件是在 UI 线程上激发的,所以您根本不必担心代码中的线程处理问题。 
 
下面的示例展示了如何创建 Task 对象。现假设UI 有两个按钮,一个用于启动运算,一个用于停止运算,还有一个进度栏显示当前的计算进度。 
复制  保存
// 创建任务管理对象
_Task = new Task();
// 挂接任务管理对象工作状态变化事件
_Task.TaskStatusChanged += new TaskEventHandler(OnTaskStatusChanged);
// 挂接任务管理对象工作进度变化事件
_Task.TaskProgressChanged += new TaskEventHandler(OnTaskProgressChanged);
 
(1) 
用于计算状态和计算进度事件的事件处理程序相应地更新 UI,例如通过更新状态栏控件。 
复制  保存
private void OnTaskProgressChanged(object sender, TaskEventArgs e)
{
_progressBar.Value = e.Progress;
}
 
(2) 
下面的代码展示的 TaskStatusChanged 事件处理程序更新进度栏的值以反映当前的计算进度。假定进度栏的最小值和最大值已经初始化。 
复制  保存
private void OnTaskStatusChanged(object sender, TaskEventArgs e)
{
switch (e.Status)
{
case TaskStatus.Running:
button1.Enabled = false;
button2.Enabled = true;
break;
case TaskStatus.Stop:
button1.Enabled = true;
button2.Enabled = false;
break;
case TaskStatus.CancelPending:
button1.Enabled = false;
button2.Enabled = false;
break;
}
}
  
在这个示例中,TaskStatusChanged 事件处理程序根据计算状态启用和禁用启动和停止按钮。这可以防止用户尝试启动一个已经在进行的计算,并且向用户提供有关计算状态的反馈。 
 
通过使用 Task 对象中的公共方法,UI 为每个按钮单击实现了窗体事件处理程序,以便启动和停止计算。例如,启动按钮事件处理程序调用 StartTask 方法,如下所示。 
复制  保存
private void startButton_Click(object sender, System.EventArgs e)
{
_Task.StartTask(new object[] { });
}
 
类似地,停止计算按钮通过调用 StopTask 方法来停止计算,如下所示。 
复制  保存
private void stopButton_Click( object sender, System.EventArgs e )
{
_Task.StopTask();
}
 
 
二.可能在非UI线程中使用Task类时 
(1)和(2)应作如下改变 
 
(1) 
用于计算状态和计算进度事件的事件处理程序相应地更新 UI,例如通过更新状态栏控件。  
复制  保存
private void OnTaskProgressChanged(object sender, TaskEventArgs e)
{
if (InvokeRequired)        //不在UI线程上,异步调用
    {
TaskEventHandler TPChanged = new TaskEventHandler(OnTaskProgressChanged);
this.BeginInvoke(TPChanged, new object[] { sender, e });
}
else                        //更新
{
_progressBar.Value = e.Progress;
}
}
 
(2) 
下面的代码展示的 TaskStatusChanged 事件处理程序更新进度栏的值以反映当前的计算进度。假定进度栏的最小值和最大值已经初始化。 
复制  保存
private void OnTaskStatusChanged(object sender, TaskEventArgs e)
{
if (InvokeRequired)        //不在UI线程上,异步调用
    {
TaskEventHandler TSChanged = new TaskEventHandler(OnTaskStatusChanged);
this.BeginInvoke(TSChanged, new object[] { sender, e });
}
else                        //更新
{
switch (e.Status)
{
case TaskStatus.Running:
button1.Enabled = false;
button2.Enabled = true;
break;
case TaskStatus.Stop:
button1.Enabled = true;
button2.Enabled = false;
break;
case TaskStatus.CancelPending:
button1.Enabled = false;
button2.Enabled = false;
break;
}
}
}
 

三、示例
1.启动时的UI界面

单击显示全图,Ctrl+滚轮缩放图片

2.后台工作方法(费用方法)运行后,任务状态为Running

单击显示全图,Ctrl+滚轮缩放图片

3.强制中止工作方法,运行任务状态Aborted

单击显示全图,Ctrl+滚轮缩放图片

4.工作方法突发错误时,任务状态ThrowErrorStoped

单击显示全图,Ctrl+滚轮缩放图片

5.工作方法正常结束或正常取消而结束时,任务状态Stopped

单击显示全图,Ctrl+滚轮缩放图片

示例代码下载
https://files.cnblogs.com/net66/asynchui-2.rar


原文出处:http://www.chenjiliang.com/Article/View.aspx?ArticleID=2038&TypeID=98
posted on 2008-01-22 14:13  投石问路  阅读(507)  评论(0编辑  收藏  举报