Multithreading
What is a Thread
A thread is an execution path that can be proceed independently of others.
Task-Based Asynchronous Pattern (TAP)
- ref and out parameters must never be used
- It should be named NameAsync, where Name is the name the equivalent synchronous method would have
TaskCompletionSource: is a Puppet Task. You can make the Task complete at any point you like, and you can make it fault by giving it an exception at any point you like.
Convert IAsyncResult to TAP
.Net framework team made a utility method to turn IAsyncResult into a TAP version
Task t = Task<IPHostEntry>.Factory.FromAsync<string>(Dns.BeginGetHostEntry, Dns.EndGetHostEntry,hostNameOrAddress, null);
Cold and Hot Tasks
TAP specifies that all Tasks must be hot before they are returned from a method
The exception is the TaskCompletionSource\
Delaying for a Period of Time (Task.Delay)
Simplest option is to use Thread.Sleep
in conjunction with Task.Run
:
await Task.Run(() => Thread.Sleep(100));
A more efficient approach would be to set up a Timer
, then use a TaskCompletionSource
to create a Task
that we can cause to complete when the Timer
fires:
private static Task Delay(int millis)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
Timer timer = new Timer(_ => tcs.SetResult(null), null, millis, Timeout.Infinite);
tcs.Task.ContinueWith(delegate { timer.Dispose(); });
return tcs.Task;
}
Of course, this is such a useful little tool that it’s provided in the framework. It’s called Task.Delay
, and of course the framework version is more powerful, robust, and probably more efficient than mine.
Task Utilities
Task WhenAll(IEnumerable<Task> tasks);
Task<Task<T>> WhenAny(IEnumerable<Task<T>> tasks)
These are called Combinators. We can create our own combinators.
Create Task Combinator
Palette of reusable parallel behavior
Which Thread resumes Task Execution on Await
When the SynchronizationContext is different on resuming, an expensive Post is needed. In performance-critical code, or in library code where you don’t care which thread you use, you might choose not to pay that performance penalty. That’s done by calling ConfigureAwait on the Task before awaiting it. If you do that, it won’t Post back to the original SynchronizationContext when resuming.
byte[] bytes = await client.DownloadDataTaskAsync(url).ConfigureAwait(false);