Live data from Hacker News

Asynchronous Programming in C#

github.com

101–110 of 186 posts

Re: Asynchronous Programming in C#

#101
post #6

We use async/await pretty much universally throughout our codebase today. One thing to keep in mind is that this mode of programming is actually not the most performant way to handle many problems. It is simply the most expedient way to manage I/O and spread trivial things across many cores in large, complex codebases. You can typically retrofit an existing code pile to be async-capable without a whole lot of sufferi…

This seems to be conflating several issues. Async just means non-blocking. Queue a unit of work (ie: Task) to the runtime scheduler and come back to it later.

How you implement that can be with the underlying async/await or with your own custom framework. There are many examples like Actor frameworks (Akka.net, Microsoft Orleans) or System.Channels or anything else.

You don't need a rewrite from zero, it's pretty easy to have a class with a while(true) loop contained in an async function processing things from a System.Channel and that will handle things on a single thread, while you enqueue work from anywhere. You can even use the BackgroundService base class to start from: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/ho...

Re: Asynchronous Programming in C#

#102
Am I completely crazy in thinking that we use the terms sync and async incorrectly in software?

Synchronous: Simultaneous, at the same time.

Asynchronous: Not Synchronous

So the basic category would be something like serial vs not serial where the "not serial" part consists of two approaches, synchronous, threads or forks for instance, and asynchronous, selectors and callbacks.

Right?...RIGHT?!?! Why do we refer to blocking calls as "sync"?!?!

Re: Asynchronous Programming in C#

#103
post #41

Earlier quoted context omitted.

I think it's clear from the languages given that the added complexity is to handle UI workflows, no?

You can have UI workflows just as well with a language that has (runtime-managed) green threads, like Go and Elixir. It's just that you sometimes have to make sure that certain green threads have their actions scheduled on the UI (OS) thread for backwards compatibility reasons. For example, the Scala ZIO library provides the means to control where (on which OS thread) code is scheduled in a very intuitive way that do…

>you sometimes have to make sure that certain green threads have their actions scheduled on the UI (OS) thread

But that management is the crux of the issue. The complexity of hopping into and out of contexts is what is exposed with async/await and coroutine scopes and hidden by the more simple syntax.

Re: Asynchronous Programming in C#

#104

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

I recently had to make a change that converted a few functions to be async. It was certainly annoying to propagate that back in all the signatures, but the biggest issues came with having code that was designed under the assumption that the code would be synchronous. It was difficult to rework the code in ways that would avoid issues with things like the fact that if I use a member variable, call one of these functions, and then use use that variable again, there's no longer a guarantee that the variable still has the same value.

And for issues like that I don't think that having async from the start would have helped much. Because if the signature said async but everything actually completed synchronously, it's possible that people would have been more conscious about those issues, but it's also very likely that plenty of cases would be missed. Testing wouldn't expose any problems unless the implementations were swapped out for code that was actually running asynchronously.

It's not easy to call what the right approach is. The best you can do is try to guess what the most likely future is and code for that. Violating the YAGNI principle can end up adding extra work and complexity and even reduced performance for no payoff later.

Re: Asynchronous Programming in C#

#105
post #5

Earlier quoted context omitted.

I don't think there are really ways to do async programming that don't have gotchas. It's just inherently complicated. And of course C#/.NET is a bit older and also very large, it has more surface area for weird behaviour that might not be easily fixable due to backwards compatibility. Things like using void as a return type for an async function can be a nasty surprise if you're new, but this is not an issue at all…

Sure, but I'd argue that the best way to handle this is to just stick to the already existing paradigms that developers have experience with. It's not like there are any problems which can't be solved without async or tpl, and it's not like Microsoft wants to introduce a similar paradigm outside of the .net ecosystem, so it's just creating an artificial barrier and making the code harder to understand so that they ca…

JavaScript/TypeScript and C++ do implement a similar paradigm, loosely patterned on what C# does?

Re: Asynchronous Programming in C#

#106

Earlier quoted context omitted.

> 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 contex…

> There are no context switches: async/await isn't threads.

By "context switch", I didn't meant to imply "hardware thread context switch", just the general sense of "spend some CPU time messing about with scheduling".

There is overhead to async in that you're unwinding the stack, bouncing to the thread pool scheduler, loading variables from the heap (since your async code was compiled to closures) back onto the stack, etc.

As far as I know, it's always possible to complete some given set of work in less total time (i.e. highest throughput) using a carefully hand-written multithreaded program than it is using async. Of course, most people don't have the luxury of writing and maintaining that program, so async code can often be a net win to both throughput and latency, but the overhead is there.

It's analogous to going from a manually-memory language to a language with GC. The GC makes your life easier and makes it much easier to write programs that are generally efficient, but it does incur some level of runtime overhead when compared to a program with optimally written manual alloc and free.

Re: Asynchronous Programming in C#

#107
post #57
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…

It's kind of odd that JetBrains chose async/await for Kotlin considering the JVM is going towards the Go approach for virtual threads. I guess they had to since they wanted to support android/js?

Yeah that decision make sense for multi platform. Suspending functions will work on JS and Kotlin Native.

Re: Asynchronous Programming in C#

#108

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.

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.

Re: Asynchronous Programming in C#

#109
post #64

Earlier quoted context omitted.

Always using async/await is recommended to avoid _surprising_ behavior. If a method with a signature Task Foo(); and it is not declared with async and it throws an exception, the exception is propagated directly to the call site. Think of this usage: var getBarTask = Foo(); // do some other stuff try { var bar = await getBarTask; } catch (Exception ex) { handle exceptions } Then if the Foo is not async the exception…

This surprised me. When they wrote LINQ and iterables, MS went to great lengths to ensure exceptions that could be thrown immediately (before iterating) were. I wonder why async/await are the opposite.

I don’t think I’d have a philosophical problem with throwing both synchronously and asynchronously when using async/await. After all, the act itself of queuing some work with a possible future result does seem like something that can fail. But the dotnet team (or c# compiler team, not sure) helped devs out by promising not to throw on the queuing the work bit when using async/await, and only throw at the point where the result should ordinarily be ready.

If you don’t use async/await, then I’m not sure how else they can help. By returning a task without async, the dev claims that they’re smart enough to safely kick off some async work and possibly provide a result later. But in the act of kicking off the work, you break?

Post reply on HN