C#--多线程--Task和各种任务阻塞、延续及其线程锁Lock和Task中的跨线程访问控件和UI耗时任务卡顿的解决方法

Thread线程和ThreadPool线程池

Thread:我们可以开启一个线程。但是请大家记住:线程开启会在空间和时间上有不小的开销。所以,不能随便开。

ThreadPool:会根据你的CPU的核心数开启一个最合适的线程数量。如果你操作中,非常耗时,就不要用线程池,如果耗时十几分钟,那就不合适线程池了。

Task=>Thread + ThreadPool结合 ,使用多线程,尽量使用Task

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
#region Task使用【1】多线程任务的开启3种方式

//【1】通过new的方式创建一个Task对象,并启动
static void Method1_1()
{
Task task1 = new Task(() =>
{
//在这个地方编写我们需要的逻辑...

Console.WriteLine($"new一个新的Task启动的子线程Id={Thread.CurrentThread.ManagedThreadId}");
});
task1.Start();
}

//【2】使用Task的Run()方法
static void Method1_2()
{
Task task2 = Task.Run(() =>
{
//在这个地方编写我们需要的逻辑...

Console.WriteLine($"使用Task的Run()方法开启的子线程Id={Thread.CurrentThread.ManagedThreadId}");
});
}
//1和2对比
//1,灵活开启线程,想什么时候开启就什么时候开启
//2, 马上开启线程

//【3】使用TaskFactory启动(类似于ThreadPool)
static void Method1_3()
{
Task task3 = Task.Factory.StartNew(() =>
{
//在这个地方编写我们需要的逻辑...

Console.WriteLine($"使用TaskFactory开启的子线程Id={Thread.CurrentThread.ManagedThreadId}");
});
}

#endregion

#region Task使用【2】Task的阻塞方式和任务延续

//【1】回顾之前使用Thread多个子线程执行时阻塞的方法
static void Method2()
{
Thread thread1 = new Thread(() =>
{
Thread.Sleep(2000);
Console.WriteLine("Child Thread (1)......");
});
Thread thread2 = new Thread(() =>
{
Thread.Sleep(1000);
Console.WriteLine("Child Thread (2)......");
});
thread1.Start();
thread2.Start();
//...

thread1.Join();//让调用线程阻塞
thread2.Join();
//如果有很多的thread,是不是也得有很多的Join?还有,我们只希望其中一个执行完以后,后面的其他线程就能执行,这个也做不了!

Console.WriteLine("This is Main Thread!");
}
//【2】Task各种【阻塞】方式(3个)
static void Method3()
{
Task task1 = new Task(() =>
{
Thread.Sleep(1000);
Console.WriteLine($"Task1子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
});
task1.Start();
Task task2 = new Task(() =>
{
Thread.Sleep(2000);
Console.WriteLine($"Task2子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
});
task2.Start();

////第1种方式:挨个等待和前面一样
//task1.Wait();
//task2.Wait();

////第2种方式:等待所有的任务完成 【推荐】
Task.WaitAll(task1, task2);

//第3种方式:等待任何一个完成即可 【推荐】
//Task.WaitAny(task1, task2);

Console.WriteLine("主线程开始运行!Time=" + DateTime.Now.ToLongTimeString());

/*
第2中方式结果:
Task1子线程Id=4 21:46:58
Task2子线程Id=3 21:46:59
主线程开始运行!Time=21:46:59

第3种方式结果
Task1子线程Id = 3 21:41:34
主线程开始运行!Time = 21:41:34
Task2子线程Id = 4 21:41:35
*/
}

//Task任务的延续:WhenAll 希望前面所有任务执行完毕后,再继续执行后面的线程,和前面相比,既有阻塞,又有延续。
static void Method4()
{
Task task1 = new Task(() =>
{
Thread.Sleep(1000);
Console.WriteLine($"Task1子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
});
task1.Start();
Task task2 = new Task(() =>
{
Thread.Sleep(2000);
Console.WriteLine($"Task2子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
});
task2.Start();

//线程的延续(主线程不等待,子线程依次执行,如果你需要主线程也按照子线程的顺序来,请你自己把主线程的任务放到延续任务中就可以)
//线运行主线程,然后task1和task2都执行完,再执行task3
Task.WhenAll(task1, task2).ContinueWith(task3 =>
{
//在这里可以编写你需要的业务...

Console.WriteLine($"Task3子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
});

Console.WriteLine("主线程开始运行!Time=" + DateTime.Now.ToLongTimeString());

/*
主线程开始运行!Time = 21:44:46
Task1子线程Id = 3 21:44:47
Task2子线程Id = 4 21:44:48
Task3子线程Id = 3 21:44:48
*/
}

//Task的延续:WhenAny
static void Method5()
{
Task task1 = new Task(() =>
{
Thread.Sleep(1000);
Console.WriteLine($"Task1子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
});
task1.Start();
Task task2 = new Task(() =>
{
Thread.Sleep(2000);
Console.WriteLine($"Task2子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
});
task2.Start();

//线程的延续(主线程不等待,子线程任何一个执行完毕,就会执行后面的线程)
Task.WhenAny(task1, task2).ContinueWith(task3 =>
{
//在这里可以编写你需要的业务...

Console.WriteLine($"Task3子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
});

Console.WriteLine("主线程开始运行!Time=" + DateTime.Now.ToLongTimeString());

/*
主线程开始运行!Time=21:48:51
Task1子线程Id=3 21:48:52
Task3子线程Id=6 21:48:52
Task2子线程Id=4 21:48:53
*/
}

#endregion

#region Task使用【3】Task常见枚举 TaskCreationOptions(父子任务运行、长时间运行的任务处理)

//请大家通过Task的构造方法,观察TaskCreationOptions这个枚举的类型,自己通过F12查看
static void Method6()
{
Task parentTask = new Task(() =>
{
Task task1 = new Task(() =>
{
Thread.Sleep(1000);
Console.WriteLine($"Task1子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
}, TaskCreationOptions.AttachedToParent);

Task task2 = new Task(() =>
{
Thread.Sleep(3000);
Console.WriteLine($"Task2子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
}, TaskCreationOptions.AttachedToParent);
task1.Start();
task2.Start();
});

parentTask.Start();
parentTask.Wait();//等待附加的子任务全部完成。相当于Task.WaitAll(taks1,task2);
//TaskCreationOptions.AttachedToParent如果这个枚举参数不添加,主线程会直接运行,不等待
Console.WriteLine("主线程开始执行!Time= " + DateTime.Now.ToLongTimeString());

/*
Task1子线程Id=4 21:52:17
Task2子线程Id=5 21:52:19
主线程开始执行!Time= 21:52:19
*/
}

//长时间的任务运行,需要采取的方法
static void Method7()
{
Task task1 = new Task(() =>
{
Thread.Sleep(2000);
Console.WriteLine($"Task1子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
}, TaskCreationOptions.LongRunning);

//LongRunning:如果你明确知道这个任务是长时间运行的,建议你加上。
//当然你使用Thread也是可以的。但是不要使用ThreadPool,因为长时间占用不归还线程,系统会强制开启新的线程,会一定程度影响性能
task1.Start();
task1.Wait();

Console.WriteLine("主线程开始执行!Time= " + DateTime.Now.ToLongTimeString());

/*
Task1子线程Id=3 21:57:42
主线程开始执行!Time= 21:57:42
*/
}
#endregion

#region Task使用【4】Task中的取消功能:使用的是CacellationTokenSoure解决多任务中协作取消和超时取消方法

//【1】Task任务的取消和判断
static void Method8()
{
//创建取消信号源对象
CancellationTokenSource cts = new CancellationTokenSource();
Task task = Task.Factory.StartNew(() =>
{
int i = 0;
while (!cts.IsCancellationRequested) //判断任务是否被取消
{
Thread.Sleep(200);
i++;
Console.WriteLine(
$"执行次数:{i},子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
}
}, cts.Token);

//我们在这个地方模拟一个事件产生,如果发生某个错误,就取消线程
Thread.Sleep(2000);
cts.Cancel(); //取消任务,只要传递这样一个信号就可以

/*
执行次数:1,子线程Id=3 22:06:18
执行次数:2,子线程Id=3 22:06:18
执行次数:3,子线程Id=3 22:06:18
执行次数:4,子线程Id=3 22:06:18
执行次数:5,子线程Id=3 22:06:19
执行次数:6,子线程Id=3 22:06:19
执行次数:7,子线程Id=3 22:06:19
执行次数:8,子线程Id=3 22:06:19
执行次数:9,子线程Id=3 22:06:19
执行次数:10,子线程Id=3 22:06:20
*/
}

//【2】Task任务取消:同时我们也希望做一些清理的工作,也就是取消这个动作会触发一个任务。
static void Method9()
{
CancellationTokenSource cts = new CancellationTokenSource();
Task task = Task.Factory.StartNew(() =>
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(500);

Console.WriteLine($"子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
}
}, cts.Token);

//注册一个委托:这个委托将在任务取消的时候调用
cts.Token.Register(() =>
{
//在这个地方可以编写自己要处理的逻辑...
Console.WriteLine($"任务取消,开始清理工作......{DateTime.Now.ToLongTimeString()}");
Thread.Sleep(2000);
Console.WriteLine($"任务取消,清理工作结束......{DateTime.Now.ToLongTimeString()}");
});

//这个地方肯定是有其他的逻辑来控制取消
Thread.Sleep(3000);//模拟其他的耗时工作
cts.Cancel();//取消任务

/*
子线程Id=3 22:12:52
子线程Id=3 22:12:53
子线程Id=3 22:12:53
子线程Id=3 22:12:54
子线程Id=3 22:12:54
任务取消,开始清理工作......22:12:55
子线程Id=3 22:12:55
任务取消,清理工作结束......22:12:57
*/
}

//【3】Task任务延时自动取消:比如我们请求一个远程接口,如果长时间没有返回数据,我们可以做一个时间限制,超时可以取消任务(比如微信红包退回)
static void Method10()
{
CancellationTokenSource cts = new CancellationTokenSource();
// CancellationTokenSource cts = new CancellationTokenSource(3000);
Task task = Task.Factory.StartNew(() =>
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(300);

Console.WriteLine($"子线程Id={Thread.CurrentThread.ManagedThreadId} {DateTime.Now.ToLongTimeString()}");
}
}, cts.Token);

//注册一个委托:这个委托将在任务取消的时候调用
cts.Token.Register(() =>
{
//在这个地方可以编写自己要处理的逻辑...
Console.WriteLine($"任务取消,开始清理工作......{DateTime.Now.ToLongTimeString()}");
Thread.Sleep(2000);
Console.WriteLine($"任务取消,清理工作结束......{DateTime.Now.ToLongTimeString()}");
});

cts.CancelAfter(3000); //3秒后自动取消

/*
子线程Id=3 22:16:49
子线程Id=3 22:16:50
子线程Id=3 22:16:50
子线程Id=3 22:16:50
子线程Id=3 22:16:50
子线程Id=3 22:16:51
子线程Id=3 22:16:51
子线程Id=3 22:16:51
子线程Id=3 22:16:52
任务取消,开始清理工作......22:16:52
子线程Id=3 22:16:52
任务取消,清理工作结束......22:16:54
*/
}

#endregion

#region Task使用【5】Task中专门的异常处理:AggregateException

//AggregateException:是一个异常集合,因为Task中可能抛出异常,所以我们需要新的类型来收集异常对象
static void Method11()
{
var task = Task.Factory.StartNew(() =>
{
var childTask1 = Task.Factory.StartNew(() =>
{
//实际开发中这个地方写你处理的业务,可能会发生异常....

//自己模拟一个异常
throw new Exception("my god!Exception from childTask1 happend!");
}, TaskCreationOptions.AttachedToParent);

var childTask2 = Task.Factory.StartNew(() =>
{
throw new Exception("my god!Exception from childTask2 happend!");
}, TaskCreationOptions.AttachedToParent);
});
try
{
try
{
task.Wait(); //1.异常抛出的时机(等待task执行完毕,这里是等到异常抛出)
}
catch (AggregateException ex) //2.异常所在位置
{
foreach (var item in ex.InnerExceptions)
{
Console.WriteLine(item.InnerException.Message + " " + item.GetType().Name);
}

//3.异常集合,如果你想往上抛,需要使用Handle方法处理一下
ex.Handle(p =>
{
if (p.InnerException.Message == "my god!Exception from childTask1 happend!")
return true;//就结束了,不往上抛了
else
return false; //返回false表示往上继续抛出异常
});
}
}
catch (Exception ex)
{
Console.WriteLine("-----------------------------------------------------");
Console.WriteLine(ex.InnerException.InnerException.Message);
}

/*
my god!Exception from childTask2 happend! AggregateException
my god!Exception from childTask1 happend! AggregateException
-----------------------------------------------------
my god!Exception from childTask2 happend!
*/
}

#endregion

#region 监视锁:Lock 限制线程个数的一把锁

//为什么要用锁?在多线程中,尤其是静态资源的访问,必然会有竞争

private static int nums = 0;
private static object myLock = new object();
static void Method12()
{
for (int i = 0; i < 5; i++)
{
//开启5线程调用一个nums
Task.Factory.StartNew(() =>
{
//TestMethod1();//不加锁的结果顺序是乱的,1,3,2,4,6,9,,,,500
TestMethod2();//加锁的结果顺序是对的,因为把资源给锁住了,1,2,3,4,5,6,,,,500
});
}
}

static void TestMethod1()
{
for (int i = 0; i < 100; i++)
{
nums++;
Console.WriteLine(nums);
}
}

static void TestMethod2()
{
for (int i = 0; i < 100; i++)
{
lock (myLock)
{
nums++;
Console.WriteLine(nums);
}
}
}

//Lock是Monitor语法糖,本质是解决资源的锁定问题
//我们锁住的资源一定是让线程可访问到的,所以不能是局部变量。
//锁住的资源千万不要是值类型。
//lock也不能锁住string类型。

}
#endregion

2,Task中的跨线程访问控件和UI耗时任务卡顿的解决方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//普通方法
private void btnUpdate_Click(object sender, EventArgs e)
{
Task task = new Task(() =>
{
this.lblInfo.Text = "来自Task的数据更新:我们正在学习多线程!";
});
//task.Start(); //这样使用会报错

//使用下面的方式解决报错的问题
task.Start(TaskScheduler.FromCurrentSynchronizationContext());//使用任务调度器

}

//针对UI耗时的情况,单独重载其实并不是很好
private void btnUpdate_Click1(object sender, EventArgs e)
{
Task task = new Task(() =>
{
//模拟耗时(这个地方会卡主)
Thread.Sleep(5000);//界面会卡5秒钟,多线程不是万能,多线程并不是解决卡界面的。
this.lblInfo.Text = "来自Task的数据更新:我们正在学习多线程!";
});
//task.Start(); //这样使用会报错

//使用下面的方式解决报错的问题
task.Start(TaskScheduler.FromCurrentSynchronizationContext());
}

//以后耗时任务都可以用这个方法
//针对耗时任务,我们可以使用新的方法
private void btnUpdate_Click2(object sender, EventArgs e)
{
this.btnUpdate.Enabled = false;
this.lblInfo.Text = "数据更新中,请等待......";
Task task =Task.Factory.StartNew(() =>
{
Thread.Sleep(5000); //有耗时的任务,我们可以放到ThreadPool中
});

//在ContinueWith中更新我们的数据
task.ContinueWith(t =>
{
this.lblInfo.Text = "来自Task的数据更新:我们正在学习多线程!";
this.btnUpdate.Enabled = true;
},TaskScheduler.FromCurrentSynchronizationContext()); //更新操作到同步的上下文中

}

1、Task的优势

ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便。比如:
  ◆ ThreadPool不支持线程的取消、完成、失败通知等交互性操作;
  ◆ ThreadPool不支持线程执行的先后次序;
  以往,如果开发者要实现上述功能,需要完成很多额外的工作,现在,FCL中提供了一个功能更强大的概念:Task。Task在线程池的基础上进行了优化,并提供了更多的API。在FCL4.0中,如果我们要编写多线程程序,Task显然已经优于传统的方式。
  以下是一个简单的任务示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Task t = new Task(() =>
{
Console.WriteLine("任务开始工作……");
//模拟工作过程
Thread.Sleep(5000);
});
t.Start();
t.ContinueWith((task) =>
{
Console.WriteLine("任务完成,完成时候的状态为:");
Console.WriteLine("IsCanceled={0}\tIsCompleted={1}\tIsFaulted={2}", task.IsCanceled, task.IsCompleted, task.IsFaulted);
});
Console.ReadKey();
}
}
}

2、Task的用法

2.1、创建任务
  无返回值的方式
  方式1:
  var t1 = new Task(() => TaskMethod("Task 1"));
  t1.Start();
  

1
2
3
4
5
6
7
8
  注:
  任务的状态:
  Start之前为:Created
  Start之后为:WaitingToRun

  **方式2:**
  ```Task.Run(() => TaskMethod("Task 2"));```

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

**方式3:**
  Task.Factory.StartNew(() => TaskMethod("Task 3")); 直接异步的方法
  或者
  var t3=Task.Factory.StartNew(() => TaskMethod("Task 3"));
  Task.WaitAll(t3);//等待所有任务结束
  注:
  任务的状态:
  Start之前为:Running
  Start之后为:Running

```csharp
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var t1 = new Task(() => TaskMethod("Task 1"));
var t2 = new Task(() => TaskMethod("Task 2"));
t2.Start();
t1.Start();
Task.WaitAll(t1, t2);
Task.Run(() => TaskMethod("Task 3"));
Task.Factory.StartNew(() => TaskMethod("Task 4"));
//标记为长时间运行任务,则任务不会使用线程池,而在单独的线程中运行。
Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning);

#region 常规的使用方式
Console.WriteLine("主线程执行业务处理.");
//创建任务
Task task = new Task(() =>
{
Console.WriteLine("使用System.Threading.Tasks.Task执行异步操作.");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
});
//启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
task.Start();
Console.WriteLine("主线程执行其他处理");
task.Wait();
#endregion

Thread.Sleep(TimeSpan.FromSeconds(1));
Console.ReadLine();
}

static void TaskMethod(string name)
{
Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
}
}
}

async/await的实现方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
async static void AsyncFunction()
{
await Task.Delay(1);
Console.WriteLine("使用System.Threading.Tasks.Task执行异步操作.");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(string.Format("AsyncFunction:i={0}", i));
}
}

public static void Main()
{
Console.WriteLine("主线程执行业务处理.");
AsyncFunction();
Console.WriteLine("主线程执行其他处理");
for (int i = 0; i < 10; i++)
{
Console.WriteLine(string.Format("Main:i={0}", i));
}
Console.ReadLine();
}
}
}

带返回值的方式
方式4:
  Tasktask = CreateTask(“Task 1”);
  task.Start();
  int result = task.Result;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
static Task<int> CreateTask(string name)
{
return new Task<int>(() => TaskMethod(name));
}

static void Main(string[] args)
{
TaskMethod("Main Thread Task");
Task<int> task = CreateTask("Task 1");
task.Start();
int result = task.Result;
Console.WriteLine("Task 1 Result is: {0}", result);

task = CreateTask("Task 2");
//该任务会运行在主线程中
task.RunSynchronously();
result = task.Result;
Console.WriteLine("Task 2 Result is: {0}", result);

task = CreateTask("Task 3");
Console.WriteLine(task.Status);
task.Start();

while (!task.IsCompleted)
{
Console.WriteLine(task.Status);
Thread.Sleep(TimeSpan.FromSeconds(0.5));
}

Console.WriteLine(task.Status);
result = task.Result;
Console.WriteLine("Task 3 Result is: {0}", result);

#region 常规使用方式
//创建任务
Task<int> getsumtask = new Task<int>(() => Getsum());
//启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
getsumtask.Start();
Console.WriteLine("主线程执行其他处理");
//等待任务的完成执行过程。
getsumtask.Wait();
//获得任务的执行结果
Console.WriteLine("任务执行结果:{0}", getsumtask.Result.ToString());
#endregion
}

static int TaskMethod(string name)
{
Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(TimeSpan.FromSeconds(2));
return 42;
}

static int Getsum()
{
int sum = 0;
Console.WriteLine("使用Task执行异步操作.");
for (int i = 0; i < 100; i++)
{
sum += i;
}
return sum;
}
}
}

2.2、组合任务.ContinueWith
   简单Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
public static void Main()
{
//创建一个任务
Task<int> task = new Task<int>(() =>
{
int sum = 0;
Console.WriteLine("使用Task执行异步操作.");
for (int i = 0; i < 100; i++)
{
sum += i;
}
return sum;
});
//启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
task.Start();
Console.WriteLine("主线程执行其他处理");
//任务完成时执行处理。
Task cwt = task.ContinueWith(t =>
{
Console.WriteLine("任务完成后的执行结果:{0}", t.Result.ToString());
});
task.Wait();
cwt.Wait();
}
}
}

任务的串行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
ConcurrentStack<int> stack = new ConcurrentStack<int>();

//t1先串行
var t1 = Task.Factory.StartNew(() =>
{
stack.Push(1);
stack.Push(2);
});

//t2,t3并行执行
var t2 = t1.ContinueWith(t =>
{
int result;
stack.TryPop(out result);
Console.WriteLine("Task t2 result={0},Thread id {1}", result, Thread.CurrentThread.ManagedThreadId);
});

//t2,t3并行执行
var t3 = t1.ContinueWith(t =>
{
int result;
stack.TryPop(out result);
Console.WriteLine("Task t3 result={0},Thread id {1}", result, Thread.CurrentThread.ManagedThreadId);
});

//等待t2和t3执行完
Task.WaitAll(t2, t3);

//t7串行执行
var t4 = Task.Factory.StartNew(() =>
{
Console.WriteLine("当前集合元素个数:{0},Thread id {1}", stack.Count, Thread.CurrentThread.ManagedThreadId);
});
t4.Wait();
}
}
}

子任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
public static void Main()
{
Task<string[]> parent = new Task<string[]>(state =>
{
Console.WriteLine(state);
string[] result = new string[2];
//创建并启动子任务
new Task(() => { result[0] = "我是子任务1。"; }, TaskCreationOptions.AttachedToParent).Start();
new Task(() => { result[1] = "我是子任务2。"; }, TaskCreationOptions.AttachedToParent).Start();
return result;
}, "我是父任务,并在我的处理过程中创建多个子任务,所有子任务完成以后我才会结束执行。");
//任务处理完成后执行的操作
parent.ContinueWith(t =>
{
Array.ForEach(t.Result, r => Console.WriteLine(r));
});
//启动父任务
parent.Start();
//等待任务结束 Wait只能等待父线程结束,没办法等到父线程的ContinueWith结束
//parent.Wait();
Console.ReadLine();

}
}
}

动态并行(TaskCreationOptions.AttachedToParent) 父任务等待所有子任务完成后 整个任务才算完成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Node
{
public Node Left { get; set; }
public Node Right { get; set; }
public string Text { get; set; }
}


class Program
{
static Node GetNode()
{
Node root = new Node
{
Left = new Node
{
Left = new Node
{
Text = "L-L"
},
Right = new Node
{
Text = "L-R"
},
Text = "L"
},
Right = new Node
{
Left = new Node
{
Text = "R-L"
},
Right = new Node
{
Text = "R-R"
},
Text = "R"
},
Text = "Root"
};
return root;
}

static void Main(string[] args)
{
Node root = GetNode();
DisplayTree(root);
}

static void DisplayTree(Node root)
{
var task = Task.Factory.StartNew(() => DisplayNode(root),
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default);
task.Wait();
}

static void DisplayNode(Node current)
{

if (current.Left != null)
Task.Factory.StartNew(() => DisplayNode(current.Left),
CancellationToken.None,
TaskCreationOptions.AttachedToParent,
TaskScheduler.Default);
if (current.Right != null)
Task.Factory.StartNew(() => DisplayNode(current.Right),
CancellationToken.None,
TaskCreationOptions.AttachedToParent,
TaskScheduler.Default);
Console.WriteLine("当前节点的值为{0};处理的ThreadId={1}", current.Text, Thread.CurrentThread.ManagedThreadId);
}
}
}

2.3、取消任务 CancellationTokenSource

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
private static int TaskMethod(string name, int seconds, CancellationToken token)
{
Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
for (int i = 0; i < seconds; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(1));
if (token.IsCancellationRequested) return -1;
}
return 42 * seconds;
}

private static void Main(string[] args)
{
var cts = new CancellationTokenSource();
var longTask = new Task<int>(() => TaskMethod("Task 1", 10, cts.Token), cts.Token);
Console.WriteLine(longTask.Status);
cts.Cancel();
Console.WriteLine(longTask.Status);
Console.WriteLine("First task has been cancelled before execution");
cts = new CancellationTokenSource();
longTask = new Task<int>(() => TaskMethod("Task 2", 10, cts.Token), cts.Token);
longTask.Start();
for (int i = 0; i < 5; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine(longTask.Status);
}
cts.Cancel();
for (int i = 0; i < 5; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine(longTask.Status);
}

Console.WriteLine("A task has been completed with result {0}.", longTask.Result);
}
}
}

2.4、处理任务中的异常
  单个任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
static int TaskMethod(string name, int seconds)
{
Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(TimeSpan.FromSeconds(seconds));
throw new Exception("Boom!");
return 42 * seconds;
}

static void Main(string[] args)
{
try
{
Task<int> task = Task.Run(() => TaskMethod("Task 2", 2));
int result = task.GetAwaiter().GetResult();
Console.WriteLine("Result: {0}", result);
}
catch (Exception ex)
{
Console.WriteLine("Task 2 Exception caught: {0}", ex.Message);
}
Console.WriteLine("----------------------------------------------");
Console.WriteLine();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
static int TaskMethod(string name, int seconds)
{
Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(TimeSpan.FromSeconds(seconds));
throw new Exception("Boom!");
return 42 * seconds;
}

static void Main(string[] args)
{
try
{
Task<int> task = Task.Run(() => TaskMethod("Task 2", 2));
int result = task.GetAwaiter().GetResult();
Console.WriteLine("Result: {0}", result);
}
catch (Exception ex)
{
Console.WriteLine("Task 2 Exception caught: {0}", ex.Message);
}
Console.WriteLine("----------------------------------------------");
Console.WriteLine();
}
}
}

多个任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
static int TaskMethod(string name, int seconds)
{
Console.WriteLine("Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(TimeSpan.FromSeconds(seconds));
throw new Exception(string.Format("Task {0} Boom!", name));
return 42 * seconds;
}


public static void Main(string[] args)
{
try
{
var t1 = new Task<int>(() => TaskMethod("Task 3", 3));
var t2 = new Task<int>(() => TaskMethod("Task 4", 2));
var complexTask = Task.WhenAll(t1, t2);
var exceptionHandler = complexTask.ContinueWith(t =>
Console.WriteLine("Result: {0}", t.Result),
TaskContinuationOptions.OnlyOnFaulted
);
t1.Start();
t2.Start();
Task.WaitAll(t1, t2);
}
catch (AggregateException ex)
{
ex.Handle(exception =>
{
Console.WriteLine(exception.Message);
return true;
});
}
}
}
}

2.5、Task.FromResult的应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
static IDictionary<string, string> cache = new Dictionary<string, string>()
{
{"0001","A"},
{"0002","B"},
{"0003","C"},
{"0004","D"},
{"0005","E"},
{"0006","F"},
};

public static void Main()
{
Task<string> task = GetValueFromCache("0006");
Console.WriteLine("主程序继续执行。。。。");
string result = task.Result;
Console.WriteLine("result={0}", result);

}

private static Task<string> GetValueFromCache(string key)
{
Console.WriteLine("GetValueFromCache开始执行。。。。");
string result = string.Empty;
//Task.Delay(5000);
Thread.Sleep(5000);
Console.WriteLine("GetValueFromCache继续执行。。。。");
if (cache.TryGetValue(key, out result))
{
return Task.FromResult(result);
}
return Task.FromResult("");
}

}
}

2.6、使用IProgress实现异步编程的进程通知
  IProgress只提供了一个方法void Report(T value),通过Report方法把一个T类型的值报告给IProgress,然后IProgress的实现类Progress的构造函数接收类型为Action的形参,通过这个委托让进度显示在UI界面中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
static void DoProcessing(IProgress<int> progress)
{
for (int i = 0; i <= 100; ++i)
{
Thread.Sleep(100);
if (progress != null)
{
progress.Report(i);
}
}
}

static async Task Display()
{
//当前线程
var progress = new Progress<int>(percent =>
{
Console.Clear();
Console.Write("{0}%", percent);
});
//线程池线程
await Task.Run(() => DoProcessing(progress));
Console.WriteLine("");
Console.WriteLine("结束");
}

public static void Main()
{
Task task = Display();
task.Wait();
}
}
}

2.7、Factory.FromAsync的应用 (简APM模式(委托)转换为任务)(BeginXXX和EndXXX)
  带回调方式的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
private delegate string AsynchronousTask(string threadName);

private static string Test(string threadName)
{
Console.WriteLine("Starting...");
Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(TimeSpan.FromSeconds(2));
Thread.CurrentThread.Name = threadName;
return string.Format("Thread name: {0}", Thread.CurrentThread.Name);
}

private static void Callback(IAsyncResult ar)
{
Console.WriteLine("Starting a callback...");
Console.WriteLine("State passed to a callbak: {0}", ar.AsyncState);
Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
Console.WriteLine("Thread pool worker thread id: {0}", Thread.CurrentThread.ManagedThreadId);
}

//执行的流程是 先执行Test--->Callback--->task.ContinueWith
static void Main(string[] args)
{
AsynchronousTask d = Test;
Console.WriteLine("Option 1");
Task<string> task = Task<string>.Factory.FromAsync(
d.BeginInvoke("AsyncTaskThread", Callback, "a delegate asynchronous call"), d.EndInvoke);

task.ContinueWith(t => Console.WriteLine("Callback is finished, now running a continuation! Result: {0}",
t.Result));

while (!task.IsCompleted)
{
Console.WriteLine(task.Status);
Thread.Sleep(TimeSpan.FromSeconds(0.5));
}
Console.WriteLine(task.Status);

}
}
}

不带回调方式的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
class Program
{
private delegate string AsynchronousTask(string threadName);

private static string Test(string threadName)
{
Console.WriteLine("Starting...");
Console.WriteLine("Is thread pool thread: {0}", Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(TimeSpan.FromSeconds(2));
Thread.CurrentThread.Name = threadName;
return string.Format("Thread name: {0}", Thread.CurrentThread.Name);
}

//执行的流程是 先执行Test--->task.ContinueWith
static void Main(string[] args)
{
AsynchronousTask d = Test;
Task<string> task = Task<string>.Factory.FromAsync(
d.BeginInvoke, d.EndInvoke, "AsyncTaskThread", "a delegate asynchronous call");
task.ContinueWith(t => Console.WriteLine("Task is completed, now running a continuation! Result: {0}",
t.Result));
while (!task.IsCompleted)
{
Console.WriteLine(task.Status);
Thread.Sleep(TimeSpan.FromSeconds(0.5));
}
Console.WriteLine(task.Status);

}
}
}