Reputation: 585
From the documentation
You can use await Task.Yield(); in an asynchronous method to force the method to complete asynchronously. If there is a current synchronization context (SynchronizationContext object), this will post the remainder of the method's execution back to that context. However, the context will decide how to prioritize this work relative to other work that may be pending.
So I'm expecting that, in a WebApi, I can make use of this in two ways
But I'm not finding that this is happening my my .NET Core WebApi
[HttpGet(Name = "GetWeatherForecast")]
public async Task<IEnumerable<WeatherForecast>> Get()
{
_logger.LogInformation("GET: Start DoSomethingAsync");
await DoSomethingAsync();
_logger.LogInformation("GET: End DoSomethingAsync");
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
private async Task DoSomethingAsync()
{
_logger.LogDebug("DoSomethingAsync: Starting");
await Task.Yield();
await Task.Delay(1000);
_logger.LogDebug("DoSomethingAsync: Finsihed");
}
I'm expecting "End DoSomethingAsync" to get logged first and "DoSomethingAsync: Completed" to come a second later. I know SynchronizationContext.Current
is null in this case but should my DoSomethingAsync
not log AFTER GetWeatherForecast is run?
dbug: Microsoft.Extensions.Hosting.Internal.Host[2]
Hosting started
info: AsyncTestsApi.Controllers.WeatherForecastController[0]
Start DoSomethingAsync
dbug: AsyncTestsApi.Controllers.WeatherForecastController[0]
DoSomethingAsync: Starting
dbug: AsyncTestsApi.Controllers.WeatherForecastController[0]
DoSomethingAsync: Completed
info: AsyncTestsApi.Controllers.WeatherForecastController[0]
End DoSomethingAsync
Upvotes: 0
Views: 2138
Reputation: 456322
in a WebApi, I can make use of this
I can't think of a valid use case for await Task.Yield();
on ASP.NET. It would cause a thread switch and provide no benefit.
My async operation really needs to be async and I don't want the internal implementation to return using a synchronous path.
This should never be necessary. On ASP.NET, you don't have an operation that "needs to be async". If it's synchronous, just make it synchronous.
My async operation is about to do some heavy work but I don't need to block the response to the caller. As a process, I'm "giving up" my execution to the caller and I'm happy to be scheduled later.
On ASP.NET, the response is only sent when the asynchronous method completes. So await Task.Yield();
(and await
in general) does not yield to the browser/client; it yields to the caller of the method (or the ASP.NET runtime thread pool if it was invoked by ASP.NET). See this article for more information.
If you need to return early, then you need a proper distributed architecture, as described on my blog.
Upvotes: 2
Reputation: 1062502
I'm expecting "End DoSomethingAsync" to get logged first and "DoSomethingAsync: Completed" to come a second later
That is an incorrect expectation; the await
here:
_logger.LogInformation("GET: Start DoSomethingAsync");
await DoSomethingAsync();
_logger.LogInformation("GET: End DoSomethingAsync");
means that the 3rd line doesn't happen until everything in DoSomethingAsync()
has completed asynchronously if needed. You can get the behaviour you want via:
_logger.LogInformation("GET: Start DoSomethingAsync");
var pending = DoSomethingAsync();
_logger.LogInformation("GET: End DoSomethingAsync");
await pending;
however, this is fundamentally the same as adding a second thread in regular non-async code - you now have two execution flows active on the same request. This can be valid, but can also be dangerous if thread-safety semantics are not observed.
Upvotes: 2