衔接UI线程和管理后台工作线程的类(多线程、异步调用)
2008-01-22 14:13
706 查看
一、引言
在编写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
示例代码下载
http://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
示例代码下载
http://files.cnblogs.com/net66/asynchui-2.rar
原文出处:http://www.chenjiliang.com/Article/View.aspx?ArticleID=2038&TypeID=98
相关文章推荐
- 衔接UI线程和管理后台工作线程的类(多线程、异步调用)
- 衔接UI线程和管理后台工作线程的类(多线程、异步调用)
- 衔接UI线程和管理后台工作线程的类(多线程、异步调用)
- 衔接UI线程和管理后台工作线程的类(多线程、异步调用)
- 衔接UI线程和管理后台工作线程的类(多线程、异步调用) .
- 衔接UI线程和管理后台工作线程的类(多线程、异步调用)[转]
- 衔接UI线程和管理后台工作线程的类(多线程、异步调用)
- 衔接UI线程和管理后台工作线程的类(多线程、异步调用) .
- 衔接UI线程和管理后台工作线程的类(多线程、异步调用)
- 【转】衔接UI线程和管理后台工作线程的类(多线程、异步调用)
- 衔接UI线程和管理后台工作线程的类(多线程、异步调用)
- 衔接UI线程和管理后台工作线程的类(多线程、异步调用)
- 【转】衔接UI线程和管理后台工作线程的类(多线程、异步调用)
- 衔接UI线程和管理后台工作线程的类(多线程、异步调用)
- C#多线程 定时重复调用异步线程即System.Threading.Timer类使用小例
- ui线程和后台线程异步
- Java【多线程知识总结(2)】调用setDaemon(true)变成后台线程
- C#构建多线程应用程序(1) —— 使用委托来自动创建次线程以实现异步方法的调用
- Java【多线程知识总结(2)】调用setDaemon(true)变成后台线程
- Java【多线程知识总结(2)】调用setDaemon(true)变成后台线程