Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Database operations should be async, you shouldn't consume them synchronously, as they do IO. Async/Await came out in 2012, almost a decade ago (and with great first party library support, I might add).

Moralizing aside, sometimes you do want to call async APIs as synchronous code. I don't think there should be a synchronous version of the API implemented as well, you just need to do

var myValue = DoSomethingAsync().ConfigureAwait(false).Result;

which will avoid deadlocks, and execute your call synchronously



> var myValue = DoSomethingAsync().ConfigureAwait(false).Result;

Came out a decade ago and we still don't know how to use it safely.

This example doesn't compile because there is no Result on ConfiguredTaskAwaitable. Regardless, ConfigureAwait(false) does absolutely nothing here because this Task is not being awaited.

If you're going to block this thread, you must push the work to another thread or it's going to deadlock when the implementation tries to resume a continuation (unless the implementation is 100% perfect and the SynchronizationContext smiles upon you).

var result = Task.Run(() => CalculateAsync()).GetAwaiter().GetResult();

This avoids the deadlock but can lead to other nasty things like thread pool starvation. The only true solution is to go async all the way - https://blog.stephencleary.com/2012/07/dont-block-on-async-c...


> Regardless, ConfigureAwait(false) does absolutely nothing here because this Task is not being awaited.

It does help if there is a SynchronisationContext active, like in legacy ASP.NET


No, really. ConfigureAwait configures the await. If you don't await - if you block by calling .Result - it does nothing


>> var myValue = DoSomethingAsync().ConfigureAwait(false).Result;

Doing this inside ASP.NET request processing code (e.g. a controller method) will result in thread pool starvation [1], if you see about 50-100 (the numbers are off the top of my head, so check for yourself) requests per minute hitting that line of code.

P.S.: Sorry for a medium link, but couldn't really find an alternative.

[1]: https://medium.com/criteo-engineering/net-threadpool-starvat...


"var myValue = DoSomethingAsync().ConfigureAwait(false).Result;"

In my view there should be a built-in keyword to do this right. It's too easy to get this wrong and even worse possible problems only show up rarely.


If this really needs to happen then I like .GetAwaiter().GetResult() instead of .Result to get the same exception behavior as await rather than the wrapped AggregateException that .Result throws. This is especially helpful if DoSomethingAsync sometimes throws synchronously rather than returning a task.


The reason there isn't a keyword is because its not possible to do it right. The example given is far from foolproof.


But there should be an easy way to do it right. Needing to call async functions from non-async code is not exactly an unusual thing. Or they need to make absolutely everything async which is also problematic, especially in terms of raw performance.


Well, you can put it into an extension method like this:

T SyncResult<T>(this Task<T> task){ return task.ConfigureAwait(false).Result; }

Same for ValueTask. It may already be implemented in the base .NET library as well.


To avoid deadlocks, you don't need the ConfigureAwait(false) here, but you do need it to have been applied correctly in all the async code you're calling.


Some things simply don't have an async API at the low level, e.g. DNS lookup: there is no asynchronous version of getaddrinfo(3). So if you look at the .NET sources, you'll see that Dns.GetHostEntryAsync pushes a task to a thread pool that calls getaddrinfo(3).

In the end, you arrive at a "sync top-level APIs -- async library APIs -- sync low-level OS APIs" sandwich of dubious efficiency.


There is on windows. On Linux we queue the requests asynchronously to the same address (golang does similar things)


When there's sync OS APIs what's the point of async over threads? I thought async APIs use async or polling version of syscalls and not blocking ones.


Because there are sometimes better things to do than block more threads. We can asynchronously queue dns requests to the same address (this is what we do in .NET 6)


There's plenty of database operations I don't do async. We heavily make use of ETLs. By their nature those processes are very linear and don't benefit at all from being async.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: