Live data from Hacker News

Asynchronous Programming in C#

github.com

81–90 of 186 posts

Re: Asynchronous Programming in C#

#81

Earlier quoted context omitted.

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().…

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.

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.

Re: Asynchronous Programming in C#

#82

Earlier quoted context omitted.

> This mode of programming is actually not the most performant way to handle many problems. This is correct, it's for increasing _throughput_ in concurrent scenarios. Meaning that when your server is processing multiple requests at the same time, yielding back rather than busy-waiting allows a different request to progress instead (or even to start processing a queued request earlier). When waiting for I/O with anoth…

> This is correct, it's for increasing _throughput_ in concurrent scenarios. I believe you mean the exact opposite. It decreases latency (because task B isn't blocked waiting for task A to complete) but it does so at the expense of decreased throughput. The context switches add overhead. If you just synchronously run A then B, the overall time would be shorter (higher throughput) because of less context switching ove…

If task A & B perform IO (eg, a DB call) and the alternatives are running them sequentially on one thread or running them concurrently (via async/await) on one thread, then running them concurrently can both decrease end-to-end latency and increase throughput.

> If you just synchronously run A then B, the overall time would be shorter (higher throughput) because of less context switching overhead.

There are no context switches: async/await isn't threads. The compiler generates state machines which are scheduled on a thread pool. Basically, each time an event happens (eg, database request completes or times out, or a new request arrives), that state machine is scheduled again so that it can observe that event. This doesn't involve context switching: you can have 1 thread or N threads happily working away on many concurrent tasks without needing to context switch between them.

Re: Asynchronous Programming in C#

#83

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?

Use System.Threading.Channels.

BoundedChannelFullMode.DropNewest, DropOldest, DropWrite, Wait specifies the behavior to use when writing to a bounded channel that is already full

Re: Asynchronous Programming in C#

#84
I used C#'s async/await on a project in 2017, and I took to it. I appreciated being able to follow the "relevant" parts of a method, without having to jump around to different callbacks. That being said, I think I was the only one on the project that understood it _well_. Over the course of two years, I learned lots of the same gotchas.

Avoid "async void" was one of the catchy mnemonics I learned the hard way, because one day our production server crashed because it threw an exception in an async void.

I'm working on Java web services now, and it's written using synchronous Java servlet framework (Spring/Jetty). My hidden fear is that one day we'll discover that our synchronous APIs will have to be completely re-written in the async model.

Re: Asynchronous Programming in C#

#85
Is there a rule as to which methods are best made async and which not?

Or, once you start using async would it be best to make ALL methods async?

Many methods could be either sync or async. But if you make a method that doesn't strictly need to be async async you give yourself the option of later making it actually return its result after a delay, say reading its answer from the web or asynchronously from disk.

Whereas later trying to convert sync-methods to async seems to sometimes require big changes to the structure of the whole program. If you depend on getting the answer right away there is no easy way to modify the code so it in fact returns the answer after a delay. Or is there?

A downside to async-methods I can see is that they are harder to debug of course.

Re: Asynchronous Programming in C#

#86
So much content and the `ConfigureAwait` portion, which is the BIGGEST gotcha in the whole shebang in my opinion, is not filled out?! Especially for Xamarin, you need to understand and use ConfigureAwait to properly bounce between UI / background threads.

Re: Asynchronous Programming in C#

#87

Earlier quoted context omitted.

> This mode of programming is actually not the most performant way to handle many problems. This is correct, it's for increasing _throughput_ in concurrent scenarios. Meaning that when your server is processing multiple requests at the same time, yielding back rather than busy-waiting allows a different request to progress instead (or even to start processing a queued request earlier). When waiting for I/O with anoth…

> This is correct, it's for increasing _throughput_ in concurrent scenarios. I believe you mean the exact opposite. It decreases latency (because task B isn't blocked waiting for task A to complete) but it does so at the expense of decreased throughput. The context switches add overhead. If you just synchronously run A then B, the overall time would be shorter (higher throughput) because of less context switching ove…

No, I mean that yielding allows more requests to be executed at the same time on the same number of threads, increasing throughput. Overhead of context switches is not that relevant, this is are small fry compared to e.g. waiting 100s of milliseconds (or more) for a DB or API. Yielding instead of busy-waiting, as I said above, allow another request that is ready to execute to do so sooner. This leads to higher throughput.

The other reply from reubenbond ( https://twitter.com/reubenbond ) is correct. Also the implication that async does sometimes decrease end-to-end latency because you don't have wait for request A to complete before starting request B.

async/await is not the same thing as threading, it is about using a fraction of a thread: when awaiting, "there is no thread" being used. https://blog.stephencleary.com/2013/11/there-is-no-thread.ht...

> I believe you mean the exact opposite.

As an aside, how about you say what you mean, and I'll work on what I mean.

Re: Asynchronous Programming in C#

#89

Earlier quoted context omitted.

In a vacuous sense, but in practice you almost always use asynchronous code to achieve concurrency. The canonical Microsoft tutorial on async spends about half its time talking about hiw to make your code concurrent to take advantage of async. https://docs.microsoft.com/en-us/dotnet/csharp/programming-g...

Concurrency does not require multi-threading. Maybe you mean parallelism? Concurrency can still be really valuable in the context of a single threaded application.

I did mean parallelism, but I think the point stands. There's very little practical use of async await outside of multithreading.

Re: Asynchronous Programming in C#

#90

The sync over async issue is a real common problem for me when trying to get a large older codebase converted to async and you can't just do it all at once. You still need support non async callers and you want to share code between the new async version and the old sync versions it makes it really difficult to do so. Say you have a db layer you want to move to async but you still have to support a sync api over that…

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().…

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

Post reply on HN