Live data from Hacker News

Asynchronous Programming in C#

github.com

171–180 of 186 posts

Re: Asynchronous Programming in C#

#171

The most common issue I see with async programming is that the naive style seen in most samples/docs is strictly slower than standard imperative programming for one user . In other words, it's pure overhead with no benefit at all unless you're at a very large scale and approaching 100% capacity on your hosts. Most documentation -- and most code I've seen in the wild -- reads like this: var foo = await GetFooAsync(...…

"slower" is just one piece of the calculation. On the server end one goal of async/await is that you can run 10k instances of your 3 lines of code concurrently - inside a single thread. And while this might not use parallelism to make an individual operation faster, it might use less resources overall.

The other use-case was to run multi-step operations which involve waiting on UI threads of appplications, which wouldn't have worked with blocking waits (would prevent redraw). For that use-case "speed" also isn't the highest priority.

Re: Asynchronous Programming in C#

#172
post #168
post #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, becaus…

Given that you understand it well -- do you know why the compiler accepts async void in the first place?

I think it made sense for UI integrations. From a synchronous OnClick delegate you could start an async void function - which essentially starts a background task that lives even after the click handler returns. Returning a Task here would not have made sense since nothing awaits it. But arguably the use-case could also have been fulfilled by calling `Task.Run` in the handler to spawn a background task.

Re: Asynchronous Programming in C#

#173
post #168

Earlier quoted context omitted.

Given that you understand it well -- do you know why the compiler accepts async void in the first place?

I think it made sense for UI integrations. From a synchronous OnClick delegate you could start an async void function - which essentially starts a background task that lives even after the click handler returns. Returning a Task here would not have made sense since nothing awaits it. But arguably the use-case could also have been fulfilled by calling `Task.Run` in the handler to spawn a background task.

Without knowing what the underlying method does, the async method may block the UI thread because until the first await which doesn't immediately continue it runs on the UI thread.

Re: Asynchronous Programming in C#

#174
post #90

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

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

> 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

Re: Asynchronous Programming in C#

#175

http://joeduffyblog.com/2015/11/19/asynchronous-everything/ >We were able to share this experience with .NET in time for C#’s await to ship. Sadly, by then, .NET’s Task had already been made a class. Since .NET requires async method return types to be Tasks, they cannot be zero-allocation unless you go out of your way to use clumsy patterns like caching singleton Task objects.

.NET Core and later has ValueTask for that usecase.

ValueTask is also available in Framework via System.Threading.Tasks.Extensions NuGET package.

Re: Asynchronous Programming in C#

#176

The most common issue I see with async programming is that the naive style seen in most samples/docs is strictly slower than standard imperative programming for one user . In other words, it's pure overhead with no benefit at all unless you're at a very large scale and approaching 100% capacity on your hosts. Most documentation -- and most code I've seen in the wild -- reads like this: var foo = await GetFooAsync(...…

"slower" is just one piece of the calculation. On the server end one goal of async/await is that you can run 10k instances of your 3 lines of code concurrently - inside a single thread. And while this might not use parallelism to make an individual operation faster, it might use less resources overall. The other use-case was to run multi-step operations which involve waiting on UI threads of appplications, which woul…

Like I said, this is a theoretical benefit that is realised only if the load is sufficiently high for the reduced overhead of async programming to provide a noticeable benefit.

For naive async code, there is a surprisingly narrow range of loads where this is true: only something like 80-99% load. Any higher and latencies start to go towards the stratosphere, or memory usage grows exponentially.

Of course, this is fixable with the appropriate use of backpressure and timeout cancellations, but I've never seen this implemented correctly and consistently anywhere. Almost all web apps in the wild fall over when load goes from 100% to 101%. They don't become 1% slower! Instead they take 30s to return a page or just start spewing 5xx errors.

For a point of comparison, Java is abandoning the complex and fragile async approach in favour of user-mode scheduled lightweight threads, which are vaguely similar in terms of efficiency, but are much easier for programmers to understand. They're also compatible with traditional threaded code.

Re: Asynchronous Programming in C#

#177

Earlier quoted context omitted.

.NET Core and later has ValueTask for that usecase.

Around 50% of my work .NET coding and I find It's becoming really hard to keep up with this. .NET more and more feels to me like the typical MS approach where they just keep cranking out new stuff without cleaning up existing stuff. Some of the new things are very good, some are half baked, and it's difficult to figure out on what side these new features are. Just lately I did some Entity Framework coding and noticed…

[deleted]

Re: Asynchronous Programming in C#

#178

Why do we need the `async` keyword? What is the difference between a function which returns a Task , and an async function which returns a Task ?

`async` tells the compiler to generate a IAsyncStateMachine implementation https://ranjeet.dev/understanding-how-async-state-machine-wo...

Exactly, `async`/`await` is in the same realm of `yield`, it tells the compiler to take your code and create a state machine out of it. And also similarly to `foreach` and LINQ it boils down to a lot of duck typing.

There are two concepts:

1) _awaiters_ offer methods that the compiler-generated code will call to schedule continuations and ask whether it is completed. The thing that you call `await` on needs to offer a `GetAwaiter()` method that returns such an awaiter. (Due to the nature of the duck typing it might also be an extension method actually, so you can make types in other assemblies retrospectively awaitable)

2) _async method builders_ offer methods to perform the state machine transitions and connect them to the result object (which is traditionally of type `Task` or `Task`). To register other types you can decorate them with the `System.Runtime.CompilerServices.AsyncMethodBuilderAttribute` attribute to tell the compiler what builder to use depending on the type you want to return in your async method.

I recommend this blog post series by Sergey Teplyakov for more details: https://devblogs.microsoft.com/premier-developer/dissecting-...>

Re: Asynchronous Programming in C#

#179
post #90

Earlier quoted context omitted.

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

> 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

Re: Asynchronous Programming in C#

#180
post #168
post #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, becaus…

Given that you understand it well -- do you know why the compiler accepts async void in the first place?

I think it was for backwards compatibility with event handlers (which need to return void)
Post reply on HN