1. You Can Add a Continuation To A Completed Task

And the continuation will be run. Given the code:

var t1 = Task.Factory.StartNew(() => { /* Noop */ });
Thread.Sleep(100); // Ensure t1 has run
Trace.Assert(t1.Status == TaskStatus.RanToCompletion);
// Now t1 is completed... we can still add a continuation:
bool continued = false;
var t2 = t1.ContinueWith(t => { continued = true; });
t2.Wait();
Trace.Assert(continued);

Neither assert will fire. Thus the continue block has executed despite being added to Task t1 after t1 had run. This does of course prevent a race between TaskFactory.StartNew returning a task that is scheduled (and therefore possibly already completed) with a call to Task.ContinueWith.

2. You Can Continue A Task Multiple Times

And the continuations run concurrently. I’m not sure that this would be useful very often (only if multiple tasks need to be started following a single initial task), but nice to know there is not an arbitrary zero or one multiplicity on continuations. Sample code:

var t1 = new Task(() => { Console.WriteLine("  This is the original task"); });
var t2 = t1.ContinueWith(t => {
    Console.WriteLine("   This is the first continiation (before sleep: thread #{0})", Thread.CurrentThread.ManagedThreadId);
    Thread.Sleep(250);
    Console.WriteLine("   This is the first continiation (after sleep: thread #{0})", Thread.CurrentThread.ManagedThreadId);
});
var t3 = t1.ContinueWith(t => {
    Console.WriteLine("   This is the second continiation (before sleep: thread #{0})", Thread.CurrentThread.ManagedThreadId);
    Thread.Sleep(250);
    Console.WriteLine("   This is the second continiation (after sleep: thread #{0})", Thread.CurrentThread.ManagedThreadId);
});
t1.Start();
Task.WaitAll(new[] { t1, t2, t3 });

Depending on the system this can give different results, but one should see the two “before sleep” messages before either of the two “after sleep” messages: both tasks are executing concurrently.

3. TaskFactory.FromAsync Starts the Task

No illustration of this, it just doesn’t say so in the documentation. I discovered this when a InvalidOperationException was thrown for starting an already complete task (only two statements later, the intermediate one adding a continuation). To be fair to the designers, there is really no reason to want to delay the execution of the task as the creation is a single statement (and if you really needed to, just use a helper (anonymous) function).