5 ms·
I would call myself an extremely experienced and knowledgable C# programmer with 10+ years of experience and even I found a few things surprising or new in this
by dustinmoris 5y ago
I would call myself an extremely experienced and knowledgable C# programmer with 10+ years of experience and even I found a few things surprising or new in this guide. I think C# async/await implementation is the biggest con on the .NET community, because it gets constantly hailed as one of the easiest ways of async programming but this guide itself proves to me that there are so many gotchas which are not obvious at all that it's actually not that easy after all. When I compare this with goroutines I do sometimes wish .NET would have a Go like model instead.
- gameswithgo 5y agoYou can get closer to the Go experience if you just ignore all the tuning knobs and not try to optimize performance. .NET 6 is reducing the penalty of some of the gotchas too, so things are getting better.
- fabian2k 5y agoI 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 once you know this (or if you simply used the right examples and used Task from the start). It's not a subtle gotcha. Sync over async is much sneakier and can be very nasty. But I'm not sure you can avoid this when interacting with a language/environment that used to be mostly sync and switched to async. If everything is async you don't have this issue, so this is better for newer codebases.
- only_as_i_fall 5y agoSure, 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 can post clean looking examples on their doc pages.
- contextfree 5y agoJavaScript/TypeScript and C++ do implement a similar paradigm, loosely patterned on what C# does?
- ziml77 5y agoPython and Rust as well. C# invented a large part of the design of async/await that has been adopted by other languages. I figure if there's a better way, it would have been improved on by now since those other languages had plenty of time to see the issues with C#'s version.
- metaltyphoon 5y agoMost claimed “simpler” languages are only being used for backend systems, i.e go, BEAM languages. There’s a reason that any language where UI needs to be considered has used what C# has. The flow of code is simplified because you don’t want to be on callback hell.
- only_as_i_fall 5y agoStrong agree. I've seen devs with 20 years of experience on me write silly inefficient code because they're lulled into a false sense of security by the marketing of async/await. Multithreading is one of the hardest problems in software, and Microsoft decided that the best way to solve it is to get smart and experienced people to stop forget everything they know and instead learn a bunch of opaque apis that interact with an incredibly complex internal state machine. It hardly seems worthwhile to me.
- nobodyandproud 5y agoasync/await has its place, but your application needs to be designed for it. The most illuminating moment for me was when I realized that there is no multi-threading involved with pure async/await.
- bobcostas55 5y agoasync/await is not about multithreading at all though...
- only_as_i_fall 5y agoIn 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-guide/concepts/async/ https://docs.microsoft.com/en-us/dotnet/csharp/programming-g...
- nightski 5y agoConcurrency does not require multi-threading. Maybe you mean parallelism? Concurrency can still be really valuable in the context of a single threaded application.
- joconde 5y agoIf a program wants to perform a task in an async way without delegating it to an external program (like a database server or the OS' I/O system), it has to use threads, right? I think the point is that concurrency, for some tasks, basically requires multithreading. Not for the parallelism benefits, but just to be able to make concurrency possible for a task that requires blocking a thread.
- shikharraje 5y agoEx-.NET guy here, can confirm. Coming from JVM-land I was constantly told that async await is something that makes C#/.NET much better than Java. I personally could not understand why. Async await is not as easy as it looks, and most .NET programmers who I knew, would just hammer at things to make it work. "Hey, this is an HTTPClient call? Put an await in front of it?" "Oh, is the IDE showing an error? Try .ConfigureAwait(false)?" "Oh, still some issue? Try putting async in the method declaration?" "Still showing an error? Remove the .ConfigureAwait() and just try async?" At some point, Visual Studio would stop showing warnings and errors, and then the code would pass review. Go is better in the sense that, at least people understand what a goroutine is and how/when to use it correctly.
- thrower123 5y agoasync/await is so much wildly better than previous version of .NET async programming. If you've ever had to deal with the IAsyncResult pattern in older .NET code, you'll never complain about await. https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/asynchronous-programming-model-apm https://docs.microsoft.com/en-us/dotnet/standard/asynchronou...
- Hawxy 5y ago> most .NET programmers who I knew, would just hammer at things to make it work. I hope the examples you listed are facetious or from the very early days of async/await in C#, otherwise I'd seriously question the skillset of the supposed .NET programmers. Visual Studio is fairly good at handling incorrect use of async/await, and in all of the examples you listed, the actual solution should've been "read the IDE error, hit the bulb and apply the automatically suggested fix", not "ignore the IDE error and smash keyboard until it works". ConfigureAwait usage is also not something you'll usually see outside of library code in modern C#.
- mmarq 5y agoDepending on the task, I find C# async/await more intuitive and complete than Go. I haven’t used Go for a while, but you can’t await a goroutine, you have to use a channel, which is more complicated than just using ‘await’. C# has channels, so you can replicate Go’s model.
- geewee 5y agoAgree as well. Not necessarily on the pros/cons list, but there's definitely a few gotchas here I didn't know about.
- horsawlarway 5y agoStrongly agree. Honestly, just the first point "Asynchrony is viral" is a huge fucking flag that this implementation sucks. It doesn't need to be viral, they just needed to make passing a continuation easy, and they failed miserably. Overall - I really like most of C#, but that async/await implementation is poor at best.
- torginus 5y agoI thought about this for a while as well, especially as I'm both a Go and .NET programmer, and made the following observation: Go and .NET have something that's viral about their IO code. In Go's case it's errors, in .NET's case it's async. Then I realised that basically all code that is async in .NET is I/O code just like all code in Go that throws runtime errors is I/O code as well. This is not a perfect heuristic, but it works 95% of the time. Which means that .NET code tends to suffer from the same issue as Go code. The solution is the same as well: Separate out the code that does logic processing from the code that does I/O. This way only a few top level functions will become async. I find that this makes my code cleaner and more testable as well.
- CuriousSkeptic 5y ago> Go and .NET have something that's viral about their IO code. This is basically what some language communities are trying to capture with “monads” (like “the IO monad”) There are some work yet on how to make such representations compose[1] (like how IEnumerable + Task = IAsyncEnumerable) but eventually we’ll probably see some form of effect systems for all such things reach mainstream languages. [1] http://okmij.org/ftp/Haskell/extensible/more.pdf http://okmij.org/ftp/Haskell/extensible/more.pdf
- zwieback 5y agoAgree but it just highlights that any kind of programming with more than one linear path of execution is hard. Before async/await, coroutines, etc. we all had to learn that the hard way. It's helpful to know what a process, thread or lightweight thread in your system is and at what cost it comes. The cost and frequency of context switches is not something you can ignore and will probably be forced to profile at some point, hopefully sooner than later. With these newer programming models there are easier ways to distribute work but unless you really dig deep and understand the basic mechanisms you will be lulled into a false sense of security. When async was first added to .Net I read through the details of how boldly the compiler re-writes my code and I was a bit shocked, like, can it really do that? Now I always keep that in mind as soon as I start typing a...
- spaetzleesser 5y ago" many gotchas which are not obvious at all that it's actually not that easy after all" Totally agree. At first look async/await is simple and straightforward but it's way to easy to mess up in subtle ways. Most people don't even notice that their code has problems until they get weird behavior in production. In general I believe they made async way too pervasive in the framework and are also inconsistent.
- lazulicurio 5y agoThe way I put it: .NET async makes the easy things easier and the hard things harder. The problem is that Task/Task<T> was the foundation for async, and it's a bad foundation. Even with the ability to write your own duck-typed awaiters (and the advent of ValueTask), the widespread use of Task means if you're writing async code you're going to have a tough time getting away from it.
- UglyToad 5y agoI think this is the crux of the matter. Since Task and the TPL predated async, iirc, people get befuddled by the parallelism Vs concurrency (if that's the correct term) parts of the Task API. Certainly the async story is a lot more complicated in desktop but it is very simple for most server scenarios, simply put "use this async call so that the thread can do other things while you wait for the db to respond" and the model in code is much preferable to callback hell.
- lazulicurio 5y agoIMO it's more fundamental than the parallelism v concurrency split. Microsoft in general has a tendency to bolt on functionality in a kinda slapdash manner when another team wants it, so you get a lot of cruft that really doesn't belong in the Task class[1] but is there because someone wanted a way to handle their special case so it just got thrown into Task. [1] See https://source.dot.net/#System.Private.CoreLib/Task.cs,045a746eb48cbaa9 https://source.dot.net/#System.Private.CoreLib/Task.cs,045a7...
- deleted 5y ago[deleted]