Skip to content

Commit 1115dd9

Browse files
committed
main
1 parent 95fdaea commit 1115dd9

File tree

4 files changed

+221
-72
lines changed

4 files changed

+221
-72
lines changed

docs/document/Modern CSharp/docs/Parallel Programming/Task/Create & Wait & Cancel.md renamed to docs/document/Modern CSharp/docs/Parallel Programming/Task/Create & Cancel.md

Lines changed: 61 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
# Create & Wait & Cancel
1+
# Start & Cancel
22

33
## Create & Start
44

55
Task can be created from the following sources:
66

77
- constructors of `Task` and `Task<T>`
88
- Task creation methods like `Task.Run` and `TaskFactory.StartNew`
9-
- `async` methods
9+
- `async` delegates
1010

1111
```cs
1212
var task = new Task(() => Console.WriteLine("hello"));
@@ -28,9 +28,9 @@ _ = Task.Factory.StartNew(() => Console.WriteLine("hello"));
2828
_ = Task.Run(() => Console.WriteLine("hello"));
2929

3030

31-
Foo(); // started automatically
31+
fooAsync(); // started automatically
3232
33-
static async void Foo()
33+
var fooAsync = async () =>
3434
{
3535
await Task.Run(() => Console.WriteLine("foo"));
3636
}
@@ -129,7 +129,7 @@ Task<int> t2 = await Task.Factory.StartNew(async () =>
129129
```
130130

131131
> [!TIP]
132-
> `Task.Run` does not require unwrap.
132+
> `Task.Run` has a implicit auto-unwrap for some of its overloads
133133
>```cs
134134
>Task<int> t = Task.Run(async () =>
135135
>{
@@ -149,6 +149,21 @@ Task<int> task = Task.FromResult(42);
149149
Console.WriteLine(task.Result); // Outputs: 42
150150
```
151151
152+
### Create as Continued Task
153+
154+
`Task.ContinueWith` creates a task after the previous task has terminated with certain condition, and it starts simultaneously.
155+
Such condition can be specified by `TaskContinuationOptions` in its overloads.
156+
157+
```cs
158+
Task.Run(async () =>
159+
{
160+
await Task.Delay(100);
161+
}).ContinueWith(prev =>
162+
{
163+
Console.WriteLine("Continued task started automatically!");
164+
}).Wait();
165+
```
166+
152167
### `Task.Run` vs `TaskFactory.StartNew`
153168

154169
- `Task.Run`
@@ -180,58 +195,10 @@ _ = Task.Factory.StartNew(
180195
);
181196
```
182197

183-
## Blocking & Non-Blocking Wait
184-
185-
Waiting a task means the execution of code is synchronous but there's a essential difference between Blocking Wait & Non-Blocking Wait
186-
187-
- accessing for `task.Result` causing the blocking wait that blocks the main thread
188-
```cs
189-
Task<int> task = new(() => 123);
190-
191-
task.Start();
192-
193-
Console.WriteLine(task.Result); // compiler knows it should wait the task blockingly // [!code highlight]
194-
// 123
195-
```
196-
- `await` operator waits the task without blocking the calling thread.
197-
```cs
198-
int foo = await Foo(); // does not block the main thread but still synchronous
199-
static async Task<int> Foo
200-
{
201-
await Task.Delay(500);
202-
return 123;
203-
}
204-
```
205-
206-
- `task.Wait`: to wait the task itself.
207-
- `Task.Wait*`: utils to wait a batch of tasks in a **blocking** manner.
208-
- `Task.When*`: utils to wait a batch of tasks in a **non-blocking** manner.
209-
210-
211-
A task must be started before awaiting, or the thread would be blocked forever.
212-
A task from `async` method is started implicitly so no worry here.
213-
214-
To wait a task synchronously, use `await` operator before a started task object.
215-
216-
```cs
217-
var task = new Task(() => Console.WriteLine("hello"));
218-
task.Start(); // must get it started!!! // [!code highlight]
219-
await task;
220-
```
221-
222-
Starting a simple task manually is quiet trivial so one should prefer `Task.Run` or `Task.Factory.StartNew`
223-
224-
```cs
225-
await Task.Factory.StartNew((object? foo) =>
226-
{
227-
Console.WriteLine(((dynamic)foo).Foo);
228-
}, new { Foo = 345 });
229-
230-
await Task.Run(() => { Console.WriteLine("hello"); });
231-
```
232-
233198
## Creation Options
234199

200+
<!--TODO: creation options-->
201+
235202
## Task Cancellation
236203

237204
### What Manage it & What Represents it
@@ -265,7 +232,7 @@ Task task = new(() =>
265232
{
266233
if (token.IsCancellationRequested)
267234
{
268-
throw new OperationCanceledException(); // does not terminate the whole program // [!code highlight]
235+
throw new OperationCanceledException(token); // does not terminate the whole program // [!code highlight]
269236
}
270237
Console.WriteLine("working with task");
271238
}
@@ -301,7 +268,27 @@ So this checking on whether cancellation suceeded requires a validation on the t
301268
<!--TODO: add example-->
302269

303270
```cs
271+
using CancellationTokenSource cts = new(5000);
272+
CancellationToken token = cts.Token;
273+
274+
Task task = Task.Factory.StartNew(() =>
275+
{
276+
while (true)
277+
{
278+
Console.WriteLine("operation...");
279+
token.ThrowIfCancellationRequested();
280+
Thread.Sleep(1000);
281+
}
282+
}, cts.Token); // it's oke, all tokens from same source are identical
304283
284+
try
285+
{
286+
task.Wait();
287+
}
288+
catch (AggregateException)
289+
{
290+
Console.WriteLine(task.Status); // Canceled
291+
}
305292
```
306293

307294
> [!NOTE]
@@ -364,6 +351,8 @@ Console.WriteLine("task finished");
364351

365352
After cancellation simply means chaining a event after previous task, and allowing access to previous task in the callback so you can do things conditionally.
366353

354+
<!--TODO: faulted example is not right, the task is not canceled correctly-->
355+
367356
```cs
368357
var cts = new CancellationTokenSource();
369358
var token = cts.Token;
@@ -395,6 +384,22 @@ Console.WriteLine("task finished");
395384

396385
You may wanted to combine tokens from different sources to perform a simultaneous cancellation even they're from different sources.
397386

387+
### Common Practice
388+
389+
```cs
390+
using (CancellationTokenSource cts = new(timeout))
391+
{
392+
try
393+
{
394+
await task(foo, cts.Token);
395+
}
396+
catch (OperationCanceledException)
397+
{
398+
Console.WriteLine("canceled");
399+
}
400+
}
401+
```
402+
398403
## Sleeping on Thread
399404

400405
- `Thread.Sleep`: pauses the thread and allows the scheduler to run another thread while sleeping, for the efficiency.

docs/document/Modern CSharp/docs/Parallel Programming/Task/Exception Handling.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,22 @@ Task task2 = Task.Run(() =>
1717
// program was not terminated
1818
```
1919

20+
## What Might be Thrown
21+
22+
<!--TODO:might throw TaskCanceledException in some cases, does OperationCancelledException thrown?-->
23+
Exception can be thrown and catched from a task for the following scenarios:
24+
25+
- `AggregateException` can be thrown from:
26+
- `task.Wait();`
27+
- `Task.Wait*`
28+
- `task.Result`
29+
- Direct exception can be thrown from:
30+
- `await Task.When*`
31+
- `await task`
32+
- `task.GetAwaiter().GetResult()`
2033

2134
## Catch in Statement
2235

23-
Exception can be thrown and catched from a task for the following scenarios:
24-
- `await task;`
25-
- `task.Wait();`
26-
- `task.Result`
27-
- `task.GetAwaiter().GetResult()`
2836

2937
Exception yield from tasks is **always** a composite exception `AggregateException` **unless the exception is `OperationCancelledException` and the task has cancelled.**
3038

@@ -88,13 +96,14 @@ catch (AggregateException ex)
8896
}
8997
```
9098

91-
## Access Exception From Task
99+
100+
## Handle in Continued Tasks
92101

93102
Task object itself can hold an `AggregateException` as a property.
94103
So you may handle them in a continued task or a post operation.
95104

96105
```cs
97-
_ = Task.Run(() => throw new AccessViolationException()).ContinueWith(prev =>
106+
_ = Task.Factory.StartNew(() => throw new AccessViolationException()).ContinueWith(prev =>
98107
{
99108
prev.Exception?.Handle(iex =>
100109
{

docs/document/Modern CSharp/docs/Parallel Programming/Task/Task Status.md

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Task Status
22

3+
**One should always access tasks status in either `try..catch` statement or continued tasks**
4+
35
## Created
46

57
Status `Created` is assigned when on task creation but usually seen on tasks created from constructors since other creation methods always start implicitly
@@ -9,23 +11,29 @@ Task task = new Task(() => { });
911
Console.WriteLine(task.Status); // Created
1012
```
1113

14+
## WaitingForActivation
15+
1216
## WaitingForRun
1317

14-
A task has been scheduled by scheduler but has not yet behun execution
18+
A task has been scheduled by scheduler but has not yet begun execution
1519

16-
## RunToCompletion
20+
## RanToCompletion
1721

18-
Implying a task has completed successfully.
19-
A task ran to end or terminated by returing a value has status `RunToCompletion`
22+
A task ran to end or terminated by `return` or ran to end has status `RanToCompletion`
2023

2124
## Canceled
2225

23-
A successful cancellation happens **when all of the following were satisfied**
24-
- `OperationCanceledException`(or its derived exception type) is thrown
26+
A successful cancellation happens **when all of the three conditions were satisfied**
27+
- `OperationCanceledException`(or its derived exception type such as `TaskCanceledException`) is thrown
2528
- `token.IsCancellationRequested` is true
26-
- `token` in closure passed to `OperationCanceledException` equals `token` as parameter
29+
- `token` in closure passed to `OperationCanceledException` equals `token` as parameter on task creation
30+
31+
There's a special case that can result in Canceled status when a task requires unwrap.
32+
If the inner task creation was Faulted because of a thrown of `OperationCanceledException`, the unwrap process would set the status of outer task to Canceled.
33+
Otherwise it would just remain Faulted.
2734

2835
```cs
36+
// compiler would choose a Task.Run(Func<Task> function) here
2937
var task = Task.Run(() =>
3038
{
3139
throw new OperationCanceledException(); // [!code highlight]
@@ -39,18 +47,37 @@ catch (AggregateException)
3947
{
4048
Console.WriteLine(task.Status); // Cancelled // [!code highlight]
4149
}
50+
// Explicit unwrap have the same behavior
51+
// async was marked here because TaskFactory.StartNew does not have such overload resolution like Task.Run
52+
Task task2 = Task.Factory.StartNew(async () =>
53+
{
54+
await Task.Delay(100);
55+
throw new Exception(); // it's another type of Exception this time // [!code highlight]
56+
}).Unwrap();
57+
58+
try
59+
{
60+
task2.Wait();
61+
}
62+
catch (AggregateException)
63+
{
64+
Console.WriteLine(task2.Status); // Faulted // [!code highlight]
65+
}
4266
```
4367

4468
## Faulted
4569

46-
Faulted happens on:
70+
Faulted happens on one of the scenarios:
4771

4872
- Any exception besides `OperationCanceledException` was thrown.
4973
- `OperationCanceledException` is thrown && (`token.IsCancellationRequested` is false || `token` in closure passed to `OperationCanceledException` != `token` as parameter)
74+
- Any exception is not `OperationCanceledException` was thrown on task creation. A wait on the task created anyway results Faulted.
75+
5076

5177
```cs
52-
var task = Task.Run(() =>
78+
Task task = Task.Factory.StartNew(async () =>
5379
{
80+
await Task.Delay(100);
5481
throw new Exception(); // not an OperationCanceledException // [!code highlight]
5582
});
5683

0 commit comments

Comments
 (0)