Chapter 14 - Concurrency and Asynchrony
Threading Basics
Tasks
Principles of Asynchrony
Asynchronous Functions in C#
Asynchronous Streams (from C# 8)
Asynchronous Patterns
Cancellation
async void Main()
{
var token = new CancellationToken();
[Link] (5000).ContinueWith (ant => [Link]()); // Tell it to cancel in two seconds.
await Foo (token);
}
// This is a simplified version of the CancellationToken type in [Link]:
class CancellationToken
{
public bool IsCancellationRequested { get; private set; }
public void Cancel() { IsCancellationRequested = true; }
public void ThrowIfCancellationRequested()
{
if (IsCancellationRequested) throw new OperationCanceledException();
}
}
async Task Foo (CancellationToken cancellationToken)
{
for (int i = 0; i < 10; i++)
{
[Link] (i);
await [Link] (1000);
[Link]();
}
}
Using the real CancellationToken
async void Main()
{
var cancelSource = new CancellationTokenSource();
[Link] (5000).ContinueWith (ant => [Link]()); // Tell it to cancel in two seconds.
await Foo ([Link]);
}
async Task Foo (CancellationToken cancellationToken)
{
for (int i = 0; i < 10; i++)
{
[Link] (i);
await [Link] (1000);
[Link]();
}
}
Using the real CancellationToken - improved version
async void Main()
{
var cancelSource = new CancellationTokenSource (5000); // This tells it to cancel in 5 seconds
await Foo ([Link]);
}
async Task Foo (CancellationToken cancellationToken)
{
for (int i = 0; i < 10; i++)
{
[Link] (i);
await [Link] (1000, cancellationToken); // Cancellation tokens propagate nicely
}
}
Progress reporting - with a delegate
async void Main()
{
Action<int> progress = i => [Link] (i + " %");
await Foo (progress);
}
Task Foo (Action<int> onProgressPercentChanged)
{
return [Link] (() =>
{
for (int i = 0; i < 1000; i++)
{
if (i % 10 == 0) onProgressPercentChanged (i / 10);
// Do something compute-bound...
}
});
}
Progress reporting - with IProgress
async void Main()
{
Action<int> progress = i => [Link] (i + " %");
await Foo (progress);
}
Task Foo (Action<int> onProgressPercentChanged)
{
return [Link] (() =>
{
for (int i = 0; i < 1000; i++)
{
if (i % 10 == 0) onProgressPercentChanged (i / 10);
// Do something compute-bound...
}
});
}
Task combinators - WhenAny
async void Main()
{
Task<int> winningTask = await [Link] (Delay1(), Delay2(), Delay3());
[Link] ("Done");
[Link] ([Link]); // 1
}
async Task<int> Delay1() { await [Link] (1000); return 1; }
async Task<int> Delay2() { await [Link] (2000); return 2; }
async Task<int> Delay3() { await [Link] (3000); return 3; }
Task combinators - WhenAny - await winning task
async void Main()
{
Task<int> winningTask = await [Link] (Delay1(), Delay2(), Delay3());
[Link] ("Done");
[Link] (await winningTask); // 1
}
async Task<int> Delay1() { await [Link] (1000); return 1; }
async Task<int> Delay2() { await [Link] (2000); return 2; }
async Task<int> Delay3() { await [Link] (3000); return 3; }
Task combinators - WhenAny - in one step
async void Main()
{
int answer = await await [Link] (Delay1(), Delay2(), Delay3());
[Link]();
}
async Task<int> Delay1() { await [Link] (1000); return 1; }
async Task<int> Delay2() { await [Link] (2000); return 2; }
async Task<int> Delay3() { await [Link] (3000); return 3; }
Task combinators - WhenAny - timeouts
async void Main()
{
Task<string> task = SomeAsyncFunc();
Task winner = await ([Link] (task, [Link](5000)));
if (winner != task) throw new TimeoutException();
string result = await task; // Unwrap result/re-throw
}
async Task<string> SomeAsyncFunc()
{
await [Link] (10000);
return "foo";
}
Task combinators - WhenAll
async void Main()
{
await [Link] (Delay1(), Delay2(), Delay3());
"Done".Dump();
}
async Task<int> Delay1() { await [Link] (1000); return 1; }
async Task<int> Delay2() { await [Link] (2000); return 2; }
async Task<int> Delay3() { await [Link] (3000); return 3; }
Task combinators - WhenAll - exceptions
async void Main()
{
Task task1 = [Link] (() => { throw null; } );
Task task2 = [Link] (() => { throw null; } );
Task all = [Link] (task1, task2);
try { await all; }
catch
{
[Link] ([Link]); // 2
}
}
Task combinators - WhenAll - return values
async void Main()
{
Task<int> task1 = [Link] (() => 1);
Task<int> task2 = [Link] (() => 2);
int[] results = await [Link] (task1, task2); // { 1, 2 }
[Link]();
}
Task combinators - WhenAll - web page downloads
async void Main()
{
int totalSize = await GetTotalSize ("[Link] [Link] [Link]
[Link]();
}
async Task<int> GetTotalSize (string[] uris)
{
IEnumerable<Task<byte[]>> downloadTasks = [Link] (uri => new WebClient().DownloadDataTaskAsync (uri));
byte[][] contents = await [Link] (downloadTasks);
return [Link] (c => [Link]);
}
Task combinators - WhenAll - web page downloads improved
async void Main()
{
int totalSize = await GetTotalSize ("[Link] [Link] [Link]
[Link]();
}
async Task<int> GetTotalSize (string[] uris)
{
IEnumerable<Task<int>> downloadTasks = [Link] (async uri =>
(await new WebClient().DownloadDataTaskAsync (uri)).Length);
int[] contentLengths = await [Link] (downloadTasks);
return [Link]();
}
Custom combinators - WithTimeout
async void Main()
{
string result = await SomeAsyncFunc().WithTimeout ([Link] (2));
[Link]();
}
async Task<string> SomeAsyncFunc()
{
await [Link] (10000);
return "foo";
}
public static class Extensions
{
public async static Task<TResult> WithTimeout<TResult> (this Task<TResult> task, TimeSpan timeout)
{
Task winner = await ([Link] (task, [Link] (timeout)));
if (winner != task) throw new TimeoutException();
return await task; // Unwrap result/re-throw
}
}
Custom combinators - WithCancellation
async void Main()
{
var cts = new CancellationTokenSource (3000); // Cancel after 3 seconds
string result = await SomeAsyncFunc().WithCancellation ([Link]);
[Link]();
}
async Task<string> SomeAsyncFunc()
{
await [Link] (10000);
return "foo";
}
public static class Extensions
{
public static Task<TResult> WithCancellation<TResult> (this Task<TResult> task, CancellationToken cancelToken)
{
var tcs = new TaskCompletionSource<TResult>();
var reg = [Link] (() => [Link] ());
[Link] (ant =>
{
[Link]();
if ([Link])
[Link]();
else if ([Link])
[Link] ([Link]);
else
[Link] ([Link]);
});
return [Link];
}
}
Custom combinators - WhenAllOrError
// This will throw an exception immediately.
async void Main()
{
Task<int> task1 = [Link] (() => { throw null; return 42; } );
Task<int> task2 = [Link] (5000).ContinueWith (ant => 53);
int[] results = await WhenAllOrError (task1, task2);
}
async Task<TResult[]> WhenAllOrError<TResult> (params Task<TResult>[] tasks)
{
var killJoy = new TaskCompletionSource<TResult[]>();
foreach (var task in tasks)
[Link] (ant =>
{
if ([Link])
[Link]();
else if ([Link])
[Link] ([Link]);
});
return await await [Link] ([Link], [Link] (tasks));
}
C# 12
in a Nutshell
About the Book
Code Listings
C# 12 in a Nutshell
C# 10 in a Nutshell
C# 9.0 in a Nutshell
C# 8.0 in a Nutshell
C# 7.0 in a Nutshell
Extras
Contact
Buy print or Kindle edition
Buy PDF edition
Read via O'Reilly subscription