一、引言
在编写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 )方便调用。在实际使用时,可在继承类中定义多个相同调用接口的方法,避免重复编码,较为方便。
给大家作个参考,而大牛呢,多点指正。当是扔个砖头,想砸块玉吧。
二、代码
使用 Task 类
一.在UI线程中创建Task类
Task 类负责管理后台线程。要使用 Task 类,必须做的事情就是创建一个 Task 对象,注册它激发的事件,并且实现这些事件的处理。因为事件是在 UI 线程上激发的,所以您根本不必担心代码中的线程处理问题。
下面的示例展示了如何创建 Task 对象。现假设UI 有两个按钮,一个用于启动运算,一个用于停止运算,还有一个进度栏显示当前的计算进度。
(1)
用于计算状态和计算进度事件的事件处理程序相应地更新 UI,例如通过更新状态栏控件。
(2)
下面的代码展示的 TaskStatusChanged 事件处理程序更新进度栏的值以反映当前的计算进度。假定进度栏的最小值和最大值已经初始化。
在这个示例中,TaskStatusChanged 事件处理程序根据计算状态启用和禁用启动和停止按钮。这可以防止用户尝试启动一个已经在进行的计算,并且向用户提供有关计算状态的反馈。
通过使用 Task 对象中的公共方法,UI 为每个按钮单击实现了窗体事件处理程序,以便启动和停止计算。例如,启动按钮事件处理程序调用 StartTask 方法,如下所示。
类似地,停止计算按钮通过调用 StopTask 方法来停止计算,如下所示。
二.可能在非UI线程中使用Task类时
(1)和(2)应作如下改变
(1)
用于计算状态和计算进度事件的事件处理程序相应地更新 UI,例如通过更新状态栏控件。
(2)
下面的代码展示的 TaskStatusChanged 事件处理程序更新进度栏的值以反映当前的计算进度。假定进度栏的最小值和最大值已经初始化。
三、示例
1.启动时的UI界面
2.后台工作方法(费用方法)运行后,任务状态为Running
3.强制中止工作方法,运行任务状态Aborted
4.工作方法突发错误时,任务状态ThrowErrorStoped
5.工作方法正常结束或正常取消而结束时,任务状态Stopped
示例代码下载
https://files.cnblogs.com/net66/asynchui-2.rar
原文出处:http://www.chenjiliang.com/Article/View.aspx?ArticleID=2038&TypeID=98
在编写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界面
2.后台工作方法(费用方法)运行后,任务状态为Running
3.强制中止工作方法,运行任务状态Aborted
4.工作方法突发错误时,任务状态ThrowErrorStoped
5.工作方法正常结束或正常取消而结束时,任务状态Stopped
示例代码下载
https://files.cnblogs.com/net66/asynchui-2.rar
原文出处:http://www.chenjiliang.com/Article/View.aspx?ArticleID=2038&TypeID=98