Live data from Hacker News

Asynchronous Programming in C#

github.com

121–130 of 186 posts

Re: Asynchronous Programming in C#

#121

What's the right way to do throttled async in modern C#? For some context, we have a process that needs to make an API call for each row in a file - maybe hundreds or thousands. What's the best way beyond Wait()'ing for each one to get decent performance without DOS'ing the server?

Maybe Bulkhead policy in Polly is a good match here?

Re: Asynchronous Programming in C#

#122
post #14

C# was my first exposure to async/await back in 2015 and I initially had trouble wrapping my head around various details (i.e. ConfigureAwait etc.). I think the languages that have done best job in removing all that detail are Go and Elixir (Beam based languages). Which if you pay attention removed the overhead of rewiring your brain to do async/await all the way down. I repeated async/await systems recently with Kot…

IMO ConfigureAwait is just the result of a failure to fully consider the implications of various design choices early on. To be fair, it is a tough problem, but the ergonomics ended up being horrible and the default they chose was probably the wrong default.

There are some other choices they made that are arguably not the right ones - for example, async code can do some of its initial execution on the calling thread and do the rest wherever continuations get scheduled (which is configurable...) which means you have to have exception handling in two places and the way the exception handling works will be different (the article calls this out). It is possible to avoid this by having the initial call to the async function only create the task but not run any of it - of course, there are reasons not to do it, performance being one of them, so it makes sense that they did it... it's just bad to optimize by default instead of making code simpler and more reliable.

My least favorite decision is that inexplicably, async/await state machines are very error prone... if any part of your codebase accidentally invokes a continuation twice, the state machine will potentially begin running twice or even start running again from the beginning with the same local variables. Fixing this would have been as simple as setting a bool at the end and checking it at the beginning, but for some reason they are dead-set on not fixing it. Premature optimization once again.

The existence of 'async void' is also just a complete trainwreck. They shouldn't have allowed it, especially since an 'async Task' that discards its result is just as easy.

The approach to cancellation (intrusive only) is also needlessly complex and gross. Putting a Dispose method on a Task would allow consumers of any async API to cleanly signal that they no longer need the result of a Task and any implementation would be able to observe this without anyone having to introduce a new method overload that takes a CancellationToken, not to mention that the intrusive cancellation design creates extra garbage on the heap. Really not obvious to me why they did this instead of reusing 'using x' and IDisposable.

Re: Asynchronous Programming in C#

#123
post #72

Earlier quoted context omitted.

As someone who has never really used promises or futures, what do they add, or how do they make concurrent programming easier or clearer than what's currently in Go?

In short, they provide clean API for checking on completion and error states of a long-running process from another thread without blocking that thread. This is an essential pattern for UI-related tasks, including web pages.

This is built into the .NET API though? You can just check the Faulted and Completed properties of the Task you're holding.

Re: Asynchronous Programming in C#

#124

Earlier quoted context omitted.

async/await is not for CPU-intensive parallelism. I think that's pretty much stated in the .NET docs. That's why Parallel Compute APIs like Parallel.ForEeach/For are not async. Their purpose is to enable non-blocking waits for IO, as well as to do stuff like animation on UI where you might want to execute procedural code over a larger timeframe.

The other reason those Parallel methods did use async/await is that async/await did not exist in .NET at the time those methods were introduced. But good news! The upcoming .NET 6 release will have a Parallel.ForEachAsync method: https://docs.microsoft.com/dotnet/api/system.threading.tasks...

Doing a bit of .NET archeology we find that both Task and Parallel.For can be dated to .NET 4.0 So if they wanted to, they could've included async/await support. It just didn't make sense.

Re: Asynchronous Programming in C#

#125

Earlier quoted context omitted.

The other reason those Parallel methods did use async/await is that async/await did not exist in .NET at the time those methods were introduced. But good news! The upcoming .NET 6 release will have a Parallel.ForEachAsync method: https://docs.microsoft.com/dotnet/api/system.threading.tasks...

Doing a bit of .NET archeology we find that both Task and Parallel.For can be dated to .NET 4.0 So if they wanted to, they could've included async/await support. It just didn't make sense.

Async/await were officially added to c# 5 and .net framework 4.5

I think it was possible to have async/await in .net framework 4.0 via some workarounds when it was still in CTP mode but I don't recall the details.

Re: Asynchronous Programming in C#

#126
post #72

Earlier quoted context omitted.

In short, they provide clean API for checking on completion and error states of a long-running process from another thread without blocking that thread. This is an essential pattern for UI-related tasks, including web pages.

This is built into the .NET API though? You can just check the Faulted and Completed properties of the Task you're holding.

GP wants those to be part of the Go stdlib.

Re: Asynchronous Programming in C#

#127

Earlier quoted context omitted.

The problem is that .NET has been taken over by web developers, and they expect the kind of breakneck pace of change and half-baked tools that the Javascript ecosystem has become accustomed to.

This is so true. .NET has been steadily going downhill for some time now. The best indicator is the absolutely rotten documentation for the more recent .NET stuff. Compare that to the older .NET Framework and/or Winapi documentation which was excellent.

Agreed about documentation. They produce a lot of it but it’s hard to use and doesn’t really give you the big picture. I know I am old but in the 90s and 2000s the MSDN documentation was fantastic. Sad to see it going downhill that much.

Re: Asynchronous Programming in C#

#128

Earlier quoted context omitted.

"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.

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.

Re: Asynchronous Programming in C#

#129
post #120
post #108

Earlier quoted context omitted.

It is fighting in a space which is very competitive. Java, Go, JavaScript and Python. The later two are being favorite of every university or coding camp graduate. You stay relevant or you die. Unfortunately, that implies faster dev cycles and areas like docs which are not well served.

How is .NET fighting against JavaScript or Python? Java or Go, I can understand but not JavaScript or Python.

Serverless computing is dominated by node, go, and python. .NET just doesn't startup fast enough.

Re: Asynchronous Programming in C#

#130
post #66
post #26

>Prefer async/await over directly returning Task This one seems questionable to me. I've never been bitten by any of the cons mentioned[1], and it's even noted that doing it this way does incur performance costs. I've learned over the years that if the code path is very prolific, it pays to avoid the async state machine. I'm curious if others could expand on this one. [1] https://github.com/davidfowl/AspNetCoreDiagno…

Unless you're doing awaits in a tight loop of thousands/millions of calls, the overhead of the state machine is almost non-existent, which leads to the next question, what are you doing that requires await in a tight loop of that many calls? The whole point of await is to use it to yield a thread while waiting on a long running operation, if your await returns nearly instantly then use the synchronous version and avo…

I've run into this in code that completes synchronously in the common case, but falls back to an async implementation - think caching. The simple way to write this creates a state machine even on the synchronous path.

It's possible to work around this efficiently by pulling the async code into a separate method and using ValueTask for the outer method return type.

Post reply on HN