Reputation: 2766
In my code I assume that outerFlag
will be hit after innerFlag
but it actually runs like fire and forget (innerFlag
is hit after outerFlag
). When I use Thread.Sleep
instead of Task.Delay
flags are hit in correct order.
Here is the code:
[Test]
public async Task Test() {
bool innerFlag = false;
bool outerFlag = false;
await Lock(async () => {
await Task.Delay(2000);
innerFlag = true;
});
outerFlag = true;
Thread.Sleep(1000);
Thread.Sleep(2500);
}
private Task Lock(Action action) {
return Task.Run(action);
}
I also noticed that when I call Task.Delay
and set innerFlag
without a Lock
method but by direct lambda, it works as expected.
Can somebody explain such behaviour?
Upvotes: 0
Views: 241
Reputation: 43384
Your Lock
method doesn't understand async delegates, so the async delegate you are trying to pass as argument is treated as async void
. Async voids are problematic in all sorts of ways and should be avoided, unless they are used for their intended purpose, as event handlers.
To ensure that your method understand async delegates you must create an overload that accepts a Func<Task>
as argument:
private Task Lock(Func<Task> func)
{
return Task.Run(func);
}
Notice that the func
argument can be passed directly to Task.Run
, because this method understands async delegates too. Not all built-in methods understand async delegates, with notable examples the Task.Factory.StartNew
and Parallel.ForEach
. You must be cautious every time you add the async
modifier in a delegate. You must be sure that the called method understands async delegates, or else you may end up with async voids and the havoc they create.
Upvotes: 1