一、引言
在编写Windows form时,如果直接在UI线程要运行一个费时方法的话(如从数据库查询大量数据时),会引起程序“假死”,从而导致用户不满。这个时候就需要通过多线程技术来解决,提高界面交互性能,方便用户使用。
一般通过三种方式解决:
1.通过System.Threading.Thread类,创建新的线程,Thread.Start运行费时方法。
2.通过System.Threading.ThreadPool类,将费时任务提交到线程池中,等待运行。
以上两种方法,基本思路是在UI界面中控制线程的启动和中止,在线程中回调用UI界面方法,更新界面。在线程中回调UI界面方法时,特别是涉及更新控件属性时,如果不注意,存在很大的隐患。这两种办法,编码和控制结构较为复杂,需要启动和管理额外的线程占用资源。
3.通过异步委托调用,将该方法排队到系统线程池的线程中运行,而在费时方法中也通过Control.BeginInvoke异步回调,达到"启动后不管"的目的。
这种方法,编码简单,程序结构较为清晰,充分利用.NET框架的异步委
一、引言
在编写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
示例代码下载
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步