Nick
Nick

Reputation: 389

Can I guarantee runnable task continuations have been run?

Here's a significantly reduced test case from a piece of code I'm working on:

var i = 0;

var taskCompletionSource = new TaskCompletionSource<object>();
var task = taskCompletionSource.Task;

Task.Run(async () => 
{
    await task;
    i = 1;
});

// Synchronously complete `task`
taskCompletionSource.SetResult(null);

// ???

Console.WriteLine(i);

Assuming that this code runs in the context of an async method, what should replace // ??? to ensure that this code prints 1 rather than 0?

I believe I understand why as written the program will always print 0 -- the code is executing synchronously and nothing has yielded to the scheduler. But I am surprised to learn that

await Task.Yield();

doesn't suffice. Somewhat ironically (but perfectly understandably, given that it involves no asynchronous execution) neither does

await task;

On the other hand,

await Task.Delay(1);

does seem to be enough, but I'm not clear on whether that's a guarantee or an accident of timing.

To ask the question one more way: is there any (reasonable) code I can write which will guarantee that all continuations for task have run before proceeding?

Upvotes: 1

Views: 61

Answers (2)

Haukinger
Haukinger

Reputation: 10883

Can I guarantee runnable task continuations have been run?

By awaiting them.

var i = 0;

var taskCompletionSource = new TaskCompletionSource<object>();
var task = taskCompletionSource.Task;

var continuation = Task.Run(async () => 
{
    await task;
    i = 1;
});

// Synchronously complete `task`
taskCompletionSource.SetResult(null);

// wait for the continuation
await continuation;

// ouputs 1
Console.WriteLine(i);

That works if you're withing an asynchronous method. If you're not, make it asynchronous. You can technically block, too, (.Wait() instead of await) but that invites deadlocks, so be careful.

Upvotes: 2

Paulo Morgado
Paulo Morgado

Reputation: 14856

Assuming that this code runs in the context of an async method

var i = 0;

await Task.Run(async () => 
{
    await task;
    i = 1;
});

Console.WriteLine(i);

or

var i = 0;

await task;
i = 1;

Console.WriteLine(i);

Upvotes: 0

Related Questions