2

Coming from Task.Run doesn't execute and after having read TaskContinuationOptions.RunContinuationsAsynchronously and Stack Dives, I wanted to confirm that I got this right:

When used for a TaskCompletionSource:

  • If TaskCreationOptions.RunContinuationsAsynchronously is set, then TaskCompletionSource.SetResult() will send all continuations to the thread pool where they will be queued and processed, maybe similar to wrapping them all in Task.Run() calls. The continuations will be processed asynchronously, on parallel thread pool threads, not blocking the current thread/task.

  • If TaskCreationOptions.RunContinuationsAsynchronously is not set (the default, I'm assuming), then all continuations (= delegates in Task.ContinueWith() calls) are executed synchronously, one after the other on the thread that called TaskCompletionSource.SetResult(), blocking that current thread/task until all continuations have been processed, maybe similar to the way event delegates are processed.

I wrote the following test to try and confirm these assumptions:

    [TestMethod]
    public void TestTaskCreationOptions()
    {
        void callback(int i, int sleep)
        {
            Console.WriteLine($"{i} {System.Threading.Thread.CurrentThread.Description()} - before sleep {sleep}");
            System.Threading.Thread.Sleep(sleep);
            Console.WriteLine($"{i} {System.Threading.Thread.CurrentThread.Description()} - after sleep {sleep}");
        }

        void test(TaskCompletionSource<object> tcs)
        {
            Console.WriteLine($"starting: {tcs.Task.CreationOptions}");
            tcs.Task.ContinueWith((t) => callback(1,1000));
            tcs.Task.ContinueWith((t) => callback(2,300));
            tcs.Task.ContinueWith((t) => callback(3,300));

            Task.Run(() =>
            {
                callback(-1,100);
                tcs.SetResult(null);
                callback(-2,100);


            });
            Console.WriteLine("waiting");
            Thread.Sleep(5000);
            Console.WriteLine("done");
        }

        test(new TaskCompletionSource<object>( TaskCreationOptions.RunContinuationsAsynchronously));
        test(new TaskCompletionSource<object>());
    }

And here are the (annotated) results:

    starting: RunContinuationsAsynchronously
    waiting
    -1 [11, Back, Pool] - before sleep 100
    -1 [11, Back, Pool] - after sleep 100

    -2 [11, Back, Pool] - before sleep 100

    1 [12, Back, Pool] - before sleep 1000 --> continuations are correctly executed on different threads
    2 [15, Back, Pool] - before sleep 300
    3 [13, Back, Pool] - before sleep 300

    -2 [11, Back, Pool] - after sleep 100  --> continuations correctly do not block the calling task/thread

    3 [13, Back, Pool] - after sleep 300
    2 [15, Back, Pool] - after sleep 300
    1 [12, Back, Pool] - after sleep 1000

    done

    starting: None

    -1 [13, Back, Pool] - before sleep 100
    waiting
    -1 [13, Back, Pool] - after sleep 100

    -2 [13, Back, Pool] - before sleep 100

    1 [15, Back, Pool] - before sleep 1000 --> continuations are incorrectly(?!) executed on different threads
    2 [14, Back, Pool] - before sleep 300
    3 [16, Back, Pool] - before sleep 300

    -2 [13, Back, Pool] - after sleep 100

    2 [14, Back, Pool] - after sleep 300 --> continuations incorrectly(?!) do not block the calling task/thread
    3 [16, Back, Pool] - after sleep 300
    1 [15, Back, Pool] - after sleep 1000
    done

So, apparently, my 2nd assumption (without specifying the flag) was wrong: continuations are not run synchronously - at least not as I understand the term; they are executed on separate threads, so they do not block the calling thread or each other.

Why?

mike
  • 1,627
  • 1
  • 14
  • 37

0 Answers0