一、引言 在编写Windows form时,如果直接在UI线程要运行一个费时方法的话(如从数据库查询大量数据时),会引起程序“假死”,从而导致用户不满。这个时候就需要通过多线程技术来解决,提高界面交互性能,方便用户使用。 一般通过三种方式解决: 1.通过System.Threading.Thread类,创建新的线程,Thread.Start运行费时方法。 2.通过System.Threading.ThreadPool类,将费时任务提交到线程池中,等待运行。 以上两种方法,基本思路是在UI界面中控制线程的启动和中止,在线程中回调用UI界面方法,更新界面。在线程中回调UI界面方法时,特别是涉及更新控件属性时,如果不注意,存在很大的隐患。这两种办法,编码和控制结构较为复杂,需要启动和管理额外的线程占用资源。 3.通过异步委托调用,将该方法排队到系统线程池的线程中运行,而在费时方法中也通过Control.BeginInvoke异步回调,达到"启动后不管"的目的。 这种方法,编码简单,程序结构较为清晰,充分利用.NET框架的异步委托功能,但要对异步调用知识较熟悉。 相关知识点参见 现利用.NET异步委托调用功能,编写Task抽象类,以方便管理后台工作线程,衔接后台线程与UI线程的联系。该抽象类提供了调用和管理的框架,没有方法的实现细节,通过继承类、重写方法,可以实现想要的功能。主要功能如下: 1.利用异步委托调用,实际多线程,不需要单独后台线程。 2.通过委托、事件驱动,实际后台与前台UI线程的联系,实现事件广播。 3.支持正常取消后台工作方法(费时方法)运行,也可以强制中止线程。 4.能够捕获取消、强制中止和方法出错三种情况,并突发相关事件,以便进行释放资源等操作。 5.通过异步调用,在工作方法中安全调用涉及UI控件的方法。 6.自行管理工作进程状态,提供状态变化事件。 7.只要工作方法调用签名,符合定义的TaskDelegate委托接口,可通过StartTask(TaskDelegate worker ,params object[] args )方便调用。在实际使用时,可在继承类中定义多个相同调用接口的方法,避免重复编码,较为方便。
给大家作个参考,而大牛呢,多点指正。当是扔个砖头,想砸块玉吧。
二、代码
1 using System; 2 using System.Windows.Forms; 3 4 namespace Net66.AsynchThread 5 { 6 /**//// <summary> 7 /// 任务工作状态 8 /// </summary> 9 public enum TaskStatus 10 { 11 /**//// <summary> 12 /// 任务没有运行,可能是工作进程没有开始、工作进程正常结束或正常取消工作进程 13 /// </summary> 14 Stopped, 15 /**//// <summary> 16 /// 任务没有运行,被调用者强行中止 17 /// </summary> 18 Aborted, 19 /**//// <summary> 20 /// 任务没有运行,在工作进程中触发错误而中止 21 /// </summary> 22 ThrowErrorStoped, 23 /**//// <summary> 24 /// 任务运行中 25 /// </summary> 26 Running, 27 /**//// <summary> 28 /// 尝试取消工作进程中 29 /// </summary> 30 CancelPending, 31 /**//// <summary> 32 /// 强行中止工作进程中 33 /// </summary> 34 AbortPending 35 36 } 37 38 /**//// <summary> 39 /// 任务状态消息 40 /// </summary> 41 public class TaskEventArgs : EventArgs 42 { 43 /**//// <summary> 44 /// 任务运行结果 45 /// </summary> 46 public Object Result; 47 /**//// <summary> 48 /// 任务进度(0-100) 49 /// </summary> 50 public int Progress; 51 /**//// <summary> 52 /// 任务工作状态 53 /// </summary> 54 public TaskStatus Status; 55 /**//// <summary> 56 /// 任务消息文本 57 /// </summary> 58 public String Message; 59 /**//// <summary> 60 /// 创建任务状态消息 61 /// </summary> 62 /// <param name="progress">任务进度(0-100)</param> 63 public TaskEventArgs( int progress ) 64 { 65 this.Progress = progress; 66 this.Status = TaskStatus.Running; 67 } 68 /**//// <summary> 69 /// 创建任务状态消息 70 /// </summary> 71 /// <param name="status">任务线程状态</param> 72 public TaskEventArgs( TaskStatus status ) 73 { 74 this.Status = status; 75 } 76 /**//// <summary> 77 /// 创建任务状态消息 78 /// </summary> 79 /// <param name="progress">任务进度(0-100)</param> 80 /// <param name="result">任务运行中间结果</param> 81 public TaskEventArgs( int progress,object result ) 82 { 83 this.Progress = progress; 84 this.Status = TaskStatus.Running; 85 this.Result = result; 86 } 87 /**//// <summary> 88 /// 创建任务状态消息 89 /// </summary> 90 /// <param name="status">任务线程状态</param> 91 /// <param name="result">任务运行结果</param> 92 public TaskEventArgs( TaskStatus status,object result ) 93 { 94 this.Status = status; 95 this.Result = result; 96 } 97 /**//// <summary> 98 /// 创建任务状态消息 99 /// </summary> 100 /// <param name="status">任务线程状态</param> 101 /// <param name="message">消息文本</param> 102 /// <param name="result">任务运行结果</param> 103 public TaskEventArgs( TaskStatus status,string message ,object result ) 104 { 105 this.Status = status; 106 this.Message = message; 107 this.Result = result; 108 } 109 /**//// <summary> 110 /// 创建任务状态消息 111 /// </summary> 112 /// <param name="progress">任务进度(0-100)</param> 113 /// <param name="message">消息文本</param> 114 /// <param name="result">任务运行中间结果</param> 115 public TaskEventArgs( int progress,string message ,object result ) 116 { 117 this.Progress = progress; 118 this.Status = TaskStatus.Running; 119 this.Message = message; 120 this.Result = result; 121 } 122 /**//// <summary> 123 /// 创建任务状态消息 124 /// </summary> 125 /// <param name="status">任务线程状态</param> 126 /// <param name="progress">任务进度(0-100)</param> 127 /// <param name="message">消息文本</param> 128 /// <param name="result">任务运行中间结果</param> 129 public TaskEventArgs( TaskStatus status,int progress,string message ,object result ) 130 { 131 this.Status = status; 132 this.Progress = progress; 133 this.Message = message; 134 this.Result = result; 135 } 136 } 137 138 /**//// <summary> 139 /// 任务的工作方法(Work)的委托接口 140 /// 传入值:对象数组(object[]) 141 /// 返回值:对象(object) 142 /// </summary> 143 public delegate object TaskDelegate( params object[] args ); 144 145 /**//// <summary> 146 /// 任务事件的委托接口 147 /// </summary> 148 public delegate void TaskEventHandler( object sender, TaskEventArgs e ); 149 150 abstract public class Task 151 { 152 内部属性#region 内部属性 153 /**//// <summary> 154 /// 任务调用线程(前台或UI线程) 155 /// </summary> 156 protected System.Threading.Thread _callThread = null; 157 /**//// <summary> 158 /// 任务工作线程(后台) 159 /// </summary> 160 protected System.Threading.Thread _workThread = null; 161 /**//// <summary> 162 /// 任务工作状态 163 /// </summary> 164 protected TaskStatus _taskState = TaskStatus.Stopped; 165 /**//// <summary> 166 /// 任务进度(0-100) 167 /// </summary> 168 protected int _progress = -1; 169 /**//// <summary> 170 /// 任务工作结果 171 /// </summary> 172 protected object _result = null; 173 /**//// <summary> 174 /// 任务工作进程出错时,捕获的异常对象 175 /// </summary> 176 protected Exception _exception = null; 177 #endregion 178 179 事件#region 事件 180 /**//// <summary> 181 /// 任务工作状态变化事件 182 /// </summary> 183 public event TaskEventHandler TaskStatusChanged; 184 /**//// <summary> 185 /// 任务进度变化事件 186 /// </summary> 187 public event TaskEventHandler TaskProgressChanged; 188 /**//// <summary> 189 /// 任务被调用者强行中止事件 190 /// </summary> 191 public event TaskEventHandler TaskAbort; 192 /**//// <summary> 193 /// 任务工作方法执行中触发错误事件 194 /// </summary> 195 public event TaskEventHandler TaskThrowError; 196 /**//// <summary> 197 /// 任务被调用者取消事件 198 /// </summary> 199 public event TaskEventHandler TaskCancel; 200 #endregion 201 202 属性#region 属性 203 204 /**//// <summary> 205 /// 任务工作进程出错时,捕获的异常对象 206 /// </summary> 207 public Exception Exception 208 { 209 get { return _exception;} 210 } 211 /**//// <summary> 212 /// 任务调用线程(前台或UI线程) 213 /// </summary> 214 public System.Threading.Thread CallThread 215 { 216 get { return _callThread;} 217 } 218 /**//// <summary> 219 /// 任务工作线程(后台) 220 /// </summary> 221 public System.Threading.Thread WordThread 222 { 223 get {return _workThread;} 224 } 225 226 /**//// <summary> 227 /// 任务进度(0-100) 228 /// </summary> 229 public int Progress 230 { 231 get {return _progress;} 232 } 233 /**//// <summary> 234 /// 任务工作状态 235 /// </summary> 236 public TaskStatus TaskState 237 { 238 get {return _taskState;} 239 } 240 /**//// <summary> 241 /// 任务工作结果 242 /// </summary> 243 public object Result 244 { 245 get {return _result;} 246 } 247 248 protected bool IsStop 249 { 250 get 251 { 252 bool result = false; 253 switch (_taskState) 254 { 255 case TaskStatus.Stopped: 256 case TaskStatus.Aborted: 257 case TaskStatus.ThrowErrorStoped: 258 result = true; 259 break; 260 default: 261 break; 262 } 263 return result; 264 } 265 } 266 #endregion 267 268 触发事件#region 触发事件 269 /**//// <summary> 270 /// 触发任务工作状态变化事件 271 /// </summary> 272 /// <param name="status">任务工作状态</param> 273 /// <param name="result">任务工作结果对象</param> 274 protected void FireStatusChangedEvent(TaskStatus status, object result) 275 { 276 if( TaskStatusChanged != null ) 277 { 278 TaskEventArgs args = new TaskEventArgs( status,result); 279 AsyncInvoke(TaskStatusChanged,args); 280 } 281 } 282 283 /**//// <summary> 284 /// 触发任务进度变化事件 285 /// </summary> 286 /// <param name="progress">任务进度(0-100)</param> 287 /// <param name="result">任务工作中间结果对象</param> 288 protected void FireProgressChangedEvent(int progress, object result) 289 { 290 if( TaskProgressChanged != null ) 291 { 292 TaskEventArgs args = new TaskEventArgs( progress,result); 293 AsyncInvoke(TaskProgressChanged,args); 294 } 295 } 296 /**//// <summary> 297 /// 触发工作方法执行中发现错误事件 298 /// </summary> 299 /// <param name="progress">任务进度(0-100)</param> 300 /// <param name="result">任务工作中间结果对象</param> 301 protected void FireThrowErrorEvent(int progress, object result) 302 { 303 if( TaskThrowError != null ) 304 { 305 TaskEventArgs args = new TaskEventArgs( progress,result); 306 AsyncInvoke(TaskThrowError,args); 307 } 308 } 309 /**//// <summary> 310 /// 触发被调用者取消事件 311 /// </summary> 312 /// <param name="progress">任务进度(0-100)</param> 313 /// <param name="result">任务工作中间结果对象</param> 314 protected void FireCancelEvent(int progress, object result) 315 { 316 if( TaskCancel != null ) 317 { 318 TaskEventArgs args = new TaskEventArgs( progress,result); 319 AsyncInvoke(TaskCancel,args); 320 } 321 } 322 /**//// <summary> 323 /// 触发被调用者强行中止事件 324 /// </summary> 325 /// <param name="progress">任务进度(0-100)</param> 326 /// <param name="result">任务工作中间结果对象</param> 327 protected void FireAbortEvent(int progress, object result) 328 { 329 if( TaskAbort != null ) 330 { 331 TaskEventArgs args = new TaskEventArgs( progress,result); 332 AsyncInvoke(TaskAbort,args); 333 } 334 } 335 /**//// <summary> 336 /// 异步调用挂接事件委托 337 /// </summary> 338 /// <param name="eventhandler">事件处理方法句柄</param> 339 /// <param name="args">事件消息</param> 340 protected void AsyncInvoke(TaskEventHandler eventhandler,TaskEventArgs args) 341 { 342// TaskEventHandler[] tpcs = (TaskEventHandler[])eventhandler.GetInvocationList(); 343 Delegate[] tpcs = eventhandler.GetInvocationList(); 344 foreach(TaskEventHandler tpc in tpcs) 345 { 346 if ( tpc.Target is System.Windows.Forms.Control ) 347 { 348 Control targetForm = tpc.Target as System.Windows.Forms.Control; 349 targetForm.BeginInvoke( tpc,new object[] { this, args } ); 350 } 351 else 352 { 353 tpc.BeginInvoke(this, args ,null,null); //异步调用,启动后不管 354 } 355 } 356 } 357 #endregion 358 359 工作进程管理#region 工作进程管理 360 /**//// <summary> 361 /// 开启任务默认的工作进程 362 /// [public object Work(params object[] args )] 363 /// </summary> 364 /// <param name="args">传入的参数数组</param> 365 public bool StartTask( params object[] args ) 366 { 367 return StartTask(new TaskDelegate( Work ),args); 368 } 369 /**//// <summary> 370 /// 开启任务的工作进程 371 /// 将开启符合TaskDelegate委托接口的worker工作方法 372 /// </summary> 373 /// <param name="worker">工作方法</param> 374 /// <param name="args">传入的参数数组</param> 375 public bool StartTask(TaskDelegate worker ,params object[] args ) 376 { 377 bool result =false; 378 lock( this ) 379 { 380 if( IsStop && worker != null ) 381 { 382 _result = null; 383 _callThread = System.Threading.Thread.CurrentThread; 384 // 开始工作方法进程,异步开启,传送回调方法 385 worker.BeginInvoke( args ,new AsyncCallback( EndWorkBack ), worker ); 386 // 更新任务工作状态 387 _taskState = TaskStatus.Running; 388 // 触发任务工作状态变化事件 389 FireStatusChangedEvent( _taskState, null); 390 result = true; 391 } 392 } 393 return result; 394 } 395 /**//// <summary> 396 /// 请求停止任务进程 397 /// 是否停止成功,应看任务工作状态属性TaskState是否为TaskStatus.Stop 398 /// </summary> 399 public bool StopTask() 400 { 401 bool result =false; 402 lock( this ) 403 { 404 if( _taskState == TaskStatus.Running ) 405 { 406 // 更新任务工作状态 407 _taskState = TaskStatus.CancelPending; 408 // 触发任务工作状态变化事件 409 FireStatusChangedEvent( _taskState, _result); 410 result = true; 411 } 412 } 413 return result; 414 } 415 /**//// <summary> 416 /// 强行中止任务的工作线程 417 /// 418 /// </summary> 419 public bool AbortTask() 420 { 421 bool result = false; 422 lock( this ) 423 { 424 if( _taskState == TaskStatus.Running && _workThread != null ) 425 { 426 if (_workThread.ThreadState != System.Threading.ThreadState.Stopped) 427 { 428 _workThread.Abort(); 429 } 430 System.Threading.Thread.Sleep(2); 431 if (_workThread.ThreadState == System.Threading.ThreadState.Stopped) 432 { 433 // 更新任务工作状态 434 _taskState = TaskStatus.Aborted; 435 result = true; 436 } 437 else 438 { 439 // 更新任务工作状态 440 _taskState = TaskStatus.AbortPending; 441 result = false; 442 } 443 // 触发任务工作状态变化事件 444 FireStatusChangedEvent( _taskState, _result); 445 } 446 } 447 return result; 448 } 449 450 /**//// <summary> 451 /// 工作方法完成后的回调方法 452 /// 将检查是否出错,并获取、更新返回结果值 453 /// </summary> 454 /// <param name="ar">异步调用信号对象</param> 455 protected void EndWorkBack( IAsyncResult ar ) 456 { 457 bool error = false; 458 bool abort = false; 459 try //检查是否错误 460 { 461 TaskDelegate del = (TaskDelegate)ar.AsyncState; 462 _result = del.EndInvoke( ar ); 463 } 464 catch(Exception e) //如果错误,则保存错误对象 465 { 466 error = true; 467 _exception = e; 468 if (e.GetType() == typeof(System.Threading.ThreadAbortException)) 469 { 470 abort = true; 471 FireAbortEvent(_progress,_exception); 472 } 473 else 474 { 475 FireThrowErrorEvent(_progress,_exception); 476 } 477 } 478 lock( this ) 479 { 480 if (error) 481 { 482 if ( abort) 483 { 484 _taskState = TaskStatus.Aborted; //调用者强行中止 485 } 486 else 487 { 488 _taskState = TaskStatus.ThrowErrorStoped;//出现错误而中止 489 } 490 } 491 else 492 { _taskState = TaskStatus.Stopped;} //正常结束 493 FireStatusChangedEvent( _taskState, _result); 494 } 495 } 496 #endregion 497 498 工作方法的基础#region 工作方法的基础 499 /**//// <summary> 500 /// 工作方法 501 /// 在继承类中应重写(override)此方法,以实现具体的工作内容,注意以几点: 502 /// 1.须在继承类是引用base.Work,在基类(base)的Work方法中,执行线程设为IsBackground=true,并保存工作线程对象 503 /// 2.在继承类中,应及时更新_progress与_result对象,以使Progress和Result属性值正确 504 /// 3.在执行过程中应检查_taskState,以使任务中被请求停止后(_taskState为TaskStatus.CancelPending),工作线程能最快终止. 505 /// 4.如在继承类中新定义了事件,应在此方法中引用触发 506 /// 5.工作线程状态不由工作方法管理,所以在工作方法中不应改变_taskState变量值 507 /// 6.工作方法中应对args参数进行有效检查 508 /// </summary> 509 /// <param name="args">传入的参数数组</param> 510 /// <returns>返回null</returns> 511 virtual public object Work(params object[] args ) 512 { 513 System.Threading.Thread.CurrentThread.IsBackground = true; 514 _workThread = System.Threading.Thread.CurrentThread; 515 _result = null; 516 return null; 517 } 518 519 #endregion 520 } 521} 522 523 使用Task类 #region 使用Task类 524/**//* 525 526使用 Task 类 527 528一.在UI线程中创建Task类 529 530Task 类负责管理后台线程。要使用 Task 类,必须做的事情就是创建一个 Task 对象,注册它激发的事件,并且实现这些事件的处理。因为事件是在 UI 线程上激发的,所以您根本不必担心代码中的线程处理问题。 531 532下面的示例展示了如何创建 Task 对象。现假设UI 有两个按钮,一个用于启动运算,一个用于停止运算,还有一个进度栏显示当前的计算进度。 533 534// 创建任务管理对象 535_Task = new Task(); 536// 挂接任务管理对象工作状态变化事件 537_Task.TaskStatusChanged += new TaskEventHandler( OnTaskStatusChanged ); 538// 挂接任务管理对象工作进度变化事件 539_Task.TaskProgressChanged += new TaskEventHandler( OnTaskProgressChanged ); 540 541(1) 542用于计算状态和计算进度事件的事件处理程序相应地更新 UI,例如通过更新状态栏控件。 543 544private void OnTaskProgressChanged( object sender,TaskEventArgs e ) 545{ 546 _progressBar.Value = e.Progress; 547} 548(2) 549下面的代码展示的 TaskStatusChanged 事件处理程序更新进度栏的值以反映当前的计算进度。假定进度栏的最小值和最大值已经初始化。 550 551private void OnTaskStatusChanged( object sender, TaskEventArgs e ) 552{ 553 switch ( e.Status ) 554 { 555 case TaskStatus.Running: 556 button1.Enabled = false; 557 button2.Enabled = true; 558 break; 559 case TaskStatus.Stop: 560 button1.Enabled = true; 561 button2.Enabled = false; 562 break; 563 case TaskStatus.CancelPending: 564 button1.Enabled = false; 565 button2.Enabled = false; 566 break; 567 } 568} 569 570在这个示例中,TaskStatusChanged 事件处理程序根据计算状态启用和禁用启动和停止按钮。这可以防止用户尝试启动一个已经在进行的计算,并且向用户提供有关计算状态的反馈。 571 572通过使用 Task 对象中的公共方法,UI 为每个按钮单击实现了窗体事件处理程序,以便启动和停止计算。例如,启动按钮事件处理程序调用 StartTask 方法,如下所示。 573 574private void startButton_Click( object sender, System.EventArgs e ) 575{ 576 _Task.StartTask( new object[] {} ); 577} 578 579类似地,停止计算按钮通过调用 StopTask 方法来停止计算,如下所示。 580 581private void stopButton_Click( object sender, System.EventArgs e ) 582{ 583 _Task.StopTask(); 584} 585 586二.可能在非UI线程中使用Task类时 587(1)和(2)应作如下改变 588 589(1) 590用于计算状态和计算进度事件的事件处理程序相应地更新 UI,例如通过更新状态栏控件。 591 592private void OnTaskProgressChanged( object sender,TaskEventArgs e ) 593{ 594 if (InvokeRequired ) //不在UI线程上,异步调用 595 { 596 TaskEventHandler TPChanged = new TaskEventHandler( OnTaskProgressChanged ); 597 this.BeginInvoke(TPChanged,new object[] {sender,e}); 598 } 599 else //更新 600 { 601 _progressBar.Value = e.Progress; 602 } 603} 604(2) 605下面的代码展示的 TaskStatusChanged 事件处理程序更新进度栏的值以反映当前的计算进度。假定进度栏的最小值和最大值已经初始化。 606 607private void OnTaskStatusChanged( object sender, TaskEventArgs e ) 608{ 609 if (InvokeRequired ) //不在UI线程上,异步调用 610 { 611 TaskEventHandler TSChanged = new TaskEventHandler( OnTaskStatusChanged ); 612 this.BeginInvoke(TSChanged,new object[] {sender,e}); 613 } 614 else //更新 615 { 616 switch ( e.Status ) 617 { 618 case TaskStatus.Running: 619 button1.Enabled = false; 620 button2.Enabled = true; 621 break; 622 case TaskStatus.Stop: 623 button1.Enabled = true; 624 button2.Enabled = false; 625 break; 626 case TaskStatus.CancelPending: 627 button1.Enabled = false; 628 button2.Enabled = false; 629 break; 630 } 631 } 632} 633 634*/ 635#endregion 636三、示例 1.启动时的UI界面 2.后台工作方法(费用方法)运行后,任务状态为Running 3.强制中止工作方法,运行任务状态Aborted 4.工作方法突发错误时,任务状态ThrowErrorStoped 5.工作方法正常结束或正常取消而结束时,任务状态Stopped 示例代码下载 出自:http://www.cnblogs.com/net66/archive/2005/08/03/206132.html