您的位置:首页 > 产品设计 > UI/UE

衔接UI线程和管理后台工作线程的类(多线程、异步调用)

2011-03-03 12:57 531 查看

衔接UI线程和管理后台工作线程的类(多线程、异步调用)

一、引言
在编写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


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

#endregion
636


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



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



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



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



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



示例代码下载
出自:/article/6213742.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐