5 ms·
I wish he would have commented on .NET's async implementation. Microsoft really got it right here and it's arguably the best implementation I've seen. All .NET
by LennyWhiteJr 7y ago
I wish he would have commented on .NET's async implementation. Microsoft really got it right here and it's arguably the best implementation I've seen.
All .NET async APIs take an optional cancellation token parameter which solves his flow-control problem by allowing the async request to be canceled at any time. If the token is canceled, the async task will (or should) throw an OperationCanceledException which can then be cleanly handled in a standard try/catch block up the stack.
The best part about this is that it pervades the entire .NET runtime, the APIs, code examples, and has excellent documentation on correct usage patterns. Sure, a 3rd party library could choose not to support cancellation tokens, but they would be going against the entire .NET ecosystem by doing so. Every other async implementation I've seen has really seemed like a haphazard bolt-on.
I honestly don't know how I would write robust async code without cancellation tokens, so I guess he has a point when it comes to javascript and python ecosystems.
- mikewhy 7y agoThis is the direction JavaScript is going as well. Having used cancellation tokens in C#, I quite like the pattern. https://developer.mozilla.org/en-US/docs/Web/API/AbortController https://developer.mozilla.org/en-US/docs/Web/API/AbortContro...
- alpb 7y agoHow's it different than Go's context.Done() channel? Similarly it bubbles up everywhere the context is passed immediately, letting you terminate concurrent routines. I've used both .NET async/await (4 years ago) and Go ever since. The semantics and feel is nearly the same, the look is different.
- egeozcan 7y agoIMHO, being able to throw the error and it moving magically up the async stack (aka try/catch) makes it easier to get started while go makes it easier to maintain by forcing you to design a system around channels. This opinion is coming from a decade of .NET but just a few months of go experience so it's very skewed.
- Too 7y agoPython has something similar, you can cancel() any async task and it will throw a CancelledError inside the ongoing operation. I'm not sure how it would solve backpressure though, because canceling a task is normally done by the caller, while backpressure comes from the other direction of the call stack.
- the_mitsuhiko 7y agoCancel in Python and a cancellation token are very different concepts. Cancellations in Python are not passed through the same way and you can’t await a cancellation.
- tybit 7y agoYeah I agree with yourself and the the writer that cancellation is necessary. I’m not sure I agree with the writer that async should handle back pressure. To me that should be handled more explicitly by something purpose built, e.g .NETs Channel type.
- fanf2 7y agoCancellation and backpressure are different things. Backpressure is about not accepting work when there is not capacity to handle it. It is incorrect to accept too much work, hit overload, and respond to the emergency by throwing away partially completed work. That makes the overload worse by wasting effort.
- dnautics 7y agothe default backpressure response in the BEAM is that when you make a call to another actor, and your call times out because the other actor is too busy, the network temporarily is too closed, etc, etc, YOU die, with no intervention from the service you've called.
- LennyWhiteJr 7y agoTrue, but cancellation tokens allow you to alleviate backpressure by safely dropping in-flight requests once you hit a given threshold instead of adding to an unbounded scope.
- WorldMaker 7y agoOften overlooked on the backpressure side of the .NET implementation fence is the SynchronizationContext/Scheduler system. Easily overlooked because of its very apparent complexity beyond the immediate "it just works" surface level in UI event handlers in WinForms/WPF, you can do all sorts of backpressure management with .NET's Schedulers, and because of the SynchronizationContext "magic" a lot of code doesn't change and doesn't have to have any idea about and can be entirely agnostic from whatever Schedulers that are driving it. It's something that I'm both surprised and not all that surprised it hasn't made its way into more async/await languages exactly because of that confusing complexity. I believe that in Python, due to its explicit over implicit philosophy, you could almost fake SynchronizationContext/Scheduler dynamics with a smart enough "event loop" as your outermost async runner, monkey patching coroutine return Futures/Tasks to include CurrentSynchronizationContext/CurrentScheduler information. It even looks like at a shallow skim some of that is designed into the default asyncio event loop system in the form of "custom event loop policies", but in the same skim it seems like its both more complex in its own way than SynchronizationContext/Scheduler (such as its primary job seems to be in handling platform specifics), and cursory searches don't show anyone attempting to use it for backpressure management. Not that in most cases I would write backpressure management code directly in SynchronizationContext/Scheduler "magic" today: I'd use ReactiveX glue and explicitly define Schedulers there and/or make use of "off-the-shelf" RX backpressure combinators. But the low level capability at the .NET SynchronizationContext/Scheduler level is an often overlooked "magic" to async/await that can get results from existing async/await code without needing to rewrite existing async/await code. (I think the fact that RX.NET itself can piggy back off of that low level most directly as opposed to in other languages/environments implementing its scheduler support and backpressure work with less low level underlying support is also easy to overlook that .NET seems to be doing it all more cohesively under the hood.)
- cdaringe 7y agohave you tried using RX with <any language>? it solves these concerns beautifully, albeit with a possibly steep learning curve.
- nwallin 7y ago> All .NET async APIs take an optional cancellation token parameter [...] The best part about this is that it pervades the entire .NET runtime, the APIs, code examples, and has excellent documentation on correct usage patterns. This statement is completely false. The .NET runtime is inundated with bad code examples resulting in deadlocks with async code. In my professional experience, the WriteableBitmap class is completely unusable. All official code samples exhibit the same brokenness. They all tell you to do the same thing: lock() on the WriteableBitmap object, get the buffer, perform an update the buffer, unlock() on the WriteableBitmap object. But you try to lock() on a backgroundworker (the sane thing to do) and it fails because lock() can only be called from the UI thread. So you lock() on the UI thread, async into a backgroundworker (the second most sane thing to do) to update the buffer, and boom, WPF deadlocks. The official Microsoft UI framework uses .Lock(), not .TryLock(), and you are deadlocked forever. There is no solution to this problem. No solution exists. Abandon all hope ye who enter here. C#/.NET+async is horrible. Maybe this is the best async implementation that's been done, but if that's the case, the best implementation is worse than starting a new thread every time you need to block on IO. The only way to write good async C#/.NET code is to not write async C#/.NET code. But guess what? As you said, C#/.NET is inundated with async APIs, and it falls down every time you blink at it. C++ is looking to standardize async/await into the language. I think I'd rather pick up growing corn. https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.imaging.writeablebitmap https://docs.microsoft.com/en-us/dotnet/api/system.windows.m...
- BoiledCabbage 7y agoHow are you "asyncing" to the "backgroundworker" thread? Await? Task.run? Are you using a .Return? I can't see your code, but this sounds more like a misunderstanding of the requirements around a UI thread and the task scheduler than any issue with async. https://medium.com/rubrikkgroup/understanding-async-avoiding-deadlocks-e41f8f2c6f5d https://medium.com/rubrikkgroup/understanding-async-avoiding... And the below seems to be a correct implementation of your desired functionality. https://stackoverflow.com/questions/9868929/how-to-edit-a-writablebitmap-backbuffer-in-non-ui-thread https://stackoverflow.com/questions/9868929/how-to-edit-a-wr...
- 7y ago