-
Notifications
You must be signed in to change notification settings - Fork 0
/
Program.cs
209 lines (191 loc) · 8.09 KB
/
Program.cs
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
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Serilog;
namespace TaskCancellation
{
static class Program
{
public static async Task Main()
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
// Store references to the tasks so that we can wait on them and
// observe their status after cancellation.
var taskList = new List<Task>();
Console.WriteLine("Press any key to begin tasks...");
Console.ReadKey(true);
Console.WriteLine("To terminate the example, press 'c' to cancel and exit...");
Console.WriteLine();
// Request cancellation of a single task when the token source is canceled.
// Pass the token to the user delegate, and also to the task so it can
// handle the exception correctly.
var taskSpinWork = Task.Run(() => DoSomeSpinWork(1, token), token);
Console.WriteLine("Task {0} executing", taskSpinWork.Id);
taskList.Add(taskSpinWork);
// Request cancellation of a task and its children. Note the token is passed
// to (1) the user delegate and (2) as the second argument to Task.Run, so
// that the task instance can correctly handle the OperationCanceledException.
Task taskCancellationTaskOfChildren = Task.Run(() =>
{
// Create some cancelable child tasks.
Task tc;
for (int i = 3; i <= 10; i++)
{
// For each child task, pass the same token
// to each user delegate and to Task.Run.
var i1 = i;
tc = Task.Run(() => DoSomeSpinWork(i1, token), token);
Console.WriteLine("Task {0} executing", tc.Id);
taskList.Add(tc);
// Pass the same token again to do work on the parent task.
// All will be signaled by the call to tokenSource.Cancel below.
DoSomeSpinWork(2, token);
}
}, token);
await Task.Run(async () =>
await TryMultipleTask(3), token);
Console.WriteLine("Task {0} executing", taskCancellationTaskOfChildren.Id);
taskList.Add(taskCancellationTaskOfChildren);
// Request cancellation from the UI thread.
char ch = Console.ReadKey().KeyChar;
if (ch == 'c' || ch == 'C')
{
tokenSource.Cancel();
Console.WriteLine("\nTask cancellation requested.");
// Optional: Observe the change in the Status property on the task.
// It is not necessary to wait on tasks that have canceled. However,
// if you do wait, you must enclose the call in a try-catch block to
// catch the TaskCanceledExceptions that are thrown. If you do
// not wait, no exception is thrown if the token that was passed to the
// Task.Run method is the same token that requested the cancellation.
}
try
{
Task.WaitAll(taskList.ToArray());
}
catch (Exception ex)
{
Log.Error(ex, "WaitTaskError");
}
finally
{
tokenSource.Dispose();
}
// Display status of all tasks.
foreach (var task in taskList)
Log.Information("Task {0} status is now {1}", task.Id, task.Status);
}
private static async Task TryMultipleTask(int taskNumber)
{
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
token.Register(() => {
Console.WriteLine("Token cancled.");
});
// Monitor keyboard input to cancel the task.
Task.Run(() => {
Console.WriteLine("Press 'C' to cancel the task.");
if (Console.ReadKey(true).KeyChar.ToString()
.ToUpperInvariant() == "C")
tokenSource.Cancel();
});
var tasks = new List<Task>();
for (var i = 0; i < taskNumber; i++)
tasks.Add(new RandomGenerator(i.ToString()).CreateTask(token));
var exceptionCreator = new ExceptionGenerator();
tasks.Add(exceptionCreator.CreateTask<ArgumentNullException>(nameof(taskNumber)));
tasks.Add(exceptionCreator.CreateTask<NotImplementedException>("A method is yet implemented."));
var allTasks = Task.WhenAll(tasks.ToArray());
await TryCancellableTask(allTasks);
Console.WriteLine($"Task end status : {allTasks.Status}");
}
private static async Task TryCancellableTask(Task task)
{
try {
// This statement might raise exceptions.
// When only one exception was raised, await throws the exception, in
// this sample TimeoutException.
// When multiple exceptions were thrown, await throws the first exception
// but set the Task.Exception to an AggregateException wrapping all the
// exceptions thrown.
await task;
}
catch (OperationCanceledException) {
Console.WriteLine("OperationCanceledException caught...");
}
catch (TimeoutException) {
Console.WriteLine("TimeoutException caught...");
}
catch (Exception) {
Console.WriteLine("Unknown Exceptions caught");
}
ShowTaskException(task.Exception);
}
private static void ShowTaskException(AggregateException? age)
{
Console.WriteLine("Task.Exception includes [");
age?.Handle(e => {
Console.WriteLine($"{{ {e.Message} }}");
return true;
});
Console.WriteLine("]");
}
static void DoSomeSpinWork(int taskNum, CancellationToken ct)
{
// Was cancellation already requested?
if (ct.IsCancellationRequested)
{
Console.WriteLine("Task {0} was cancelled before it got started.",
taskNum);
ct.ThrowIfCancellationRequested();
}
int maxIterations = 100;
// NOTE!!! A "TaskCanceledException was unhandled
// by user code" error will be raised here if "Just My Code"
// is enabled on your computer. On Express editions JMC is
// enabled and cannot be disabled. The exception is benign.
// Just press F5 to continue executing your code.
for (int i = 0; i <= maxIterations; i++)
{
// Do a bit of work. Not too much.
var sw = new SpinWait();
for (int j = 0; j <= 100; j++)
sw.SpinOnce();
if (ct.IsCancellationRequested)
{
Console.WriteLine("Task {0} cancelled", taskNum);
ct.ThrowIfCancellationRequested();
}
}
}
}
}
// The example displays output like the following:
// Press any key to begin tasks...
// To terminate the example, press 'c' to cancel and exit...
//
// Task 1 executing
// Task 2 executing
// Task 3 executing
// Task 4 executing
// Task 5 executing
// Task 6 executing
// Task 7 executing
// Task 8 executing
// c
// Task cancellation requested.
// Task 2 cancelled
// Task 7 cancelled
//
// OperationCanceledException thrown
//
// Task 2 status is now Canceled
// Task 1 status is now RanToCompletion
// Task 8 status is now Canceled
// Task 7 status is now Canceled
// Task 6 status is now RanToCompletion
// Task 5 status is now RanToCompletion
// Task 4 status is now RanToCompletion
// Task 3 status is now RanToCompletion