衔接UI线程和管理后台工作线程的类(多线程、异步调用) (转)
时间:2010-08-23 来源:akeforever
  一、引言
       在编写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 )方便调用。在实际使用时,可在继承类中定义多个相同调用接口的方法,避免重复编码,较为方便。
给大家作个参考,而大牛呢,多点指正。当是扔个砖头,想砸块玉吧。
  
  二、代码
using System;2
using System.Windows.Forms;3

4
namespace Net66.AsynchThread5

{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
        AbortPending35

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 Task151
    
{   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
        #endregion178

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
        #endregion201

202
        属性#region 属性203
        204
        /**//// <summary>205
        /// 任务工作进程出错时,捕获的异常对象206
        /// </summary>207
        public Exception Exception208
        
{209
            get 
{ return _exception;}210
        }211
        /**//// <summary>212
        /// 任务调用线程(前台或UI线程)213
        /// </summary>214
        public System.Threading.Thread CallThread215
        
{216
            get 
{ return _callThread;}217
        }218
        /**//// <summary>219
        /// 任务工作线程(后台)220
        /// </summary>221
        public System.Threading.Thread WordThread222
        
{223
            get 
{return _workThread;}224
        }225
        226
        /**//// <summary>227
        /// 任务进度(0-100)228
        /// </summary>229
        public int Progress230
        
{231
            get 
{return _progress;} 232
        }233
        /**//// <summary>234
        /// 任务工作状态235
        /// </summary>236
        public TaskStatus TaskState237
        
{238
            get 
{return _taskState;}239
        }240
        /**//// <summary>241
        /// 任务工作结果242
        /// </summary>243
        public object Result244
        
{245
            get 
{return _result;}246
        }247

248
        protected bool IsStop249
        
{250
            get251
            
{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
        #endregion267

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
        #endregion358

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.Stop398
        /// </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
                    else438
                    
{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
                else474
                
{475
                    FireThrowErrorEvent(_progress,_exception);476
                }477
            }478
            lock( this ) 479
            
{ 480
                if (error)481
                
{482
                    if ( abort)483
                    
{484
                        _taskState = TaskStatus.Aborted;        //调用者强行中止485
                    }486
                    else487
                    
{488
                        _taskState = TaskStatus.ThrowErrorStoped;//出现错误而中止489
                    }490
                } 491
                else492
                
{    _taskState = TaskStatus.Stopped;}          //正常结束493
                FireStatusChangedEvent( _taskState, _result);494
            } 495
        } 496
        #endregion497

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
        #endregion520
    }521
}522

523
使用Task类#region 使用Task类524
/**//*525

526
使用 Task 类527

528
一.在UI线程中创建Task类529

530
Task 类负责管理后台线程。要使用 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

544
private void OnTaskProgressChanged( object sender,TaskEventArgs e ) 545
{ 546
    _progressBar.Value = e.Progress; 547
} 548
(2)549
下面的代码展示的 TaskStatusChanged 事件处理程序更新进度栏的值以反映当前的计算进度。假定进度栏的最小值和最大值已经初始化。550

551
private 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

574
private void startButton_Click( object sender, System.EventArgs e ) 575
{ 576
    _Task.StartTask( new object[] {} ); 577
} 578

579
类似地,停止计算按钮通过调用 StopTask 方法来停止计算,如下所示。580

581
private 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

592
private 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

607
private 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
#endregion636
  
  
  
  三、示例
  1.启动时的UI界面
  
  
  2.后台工作方法(费用方法)运行后,任务状态为Running
  
  
  3.强制中止工作方法,运行任务状态Aborted
  
  
  4.工作方法突发错误时,任务状态ThrowErrorStoped
  
  
  5.工作方法正常结束或正常取消而结束时,任务状态Stopped
  
http://files.cnblogs.com/net66/AsynchUI-2.rar










