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\ technique, which doesn’t really have the concept of a hot or cold task. You just need to make sure to complete the Task at some point yourself.

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);