Stephen Cleary

blog.stephencleary.com · Personal blog · on HN since 2014

Stephen Cleary writes about asynchronous programming and concurrency in C# and .NET, with some posts on Docker and GCC.

What HN saysHN commenters call his async posts brilliant and cite them for explaining async void, deadlocks, and why there is no thread in async.
29HN people shared it
23linked it in comments
7stories submitted
7story points
1 year agolast shared
2014people per year2026

Most-shared posts

  1. 1
    There is no Thread (2013)
    15 people · 4 points · first shared 2014 · discussion on HN
  2. 2
    Dont block on async code
    7 people · first shared 2014
  3. 3
    Recursive (Re-Entrant) Locks (2013)
    2 people · 1 points · first shared 2023 · discussion on HN
  4. 4
    Async and await
    2 people · first shared 2019
  5. 5
    Implementing GCC's Builtin Functions
    1 people · 1 points · first shared 2020 · discussion on HN
  6. 6
    Docker as a Tool Provider
    1 people · 1 points · first shared 2018 · discussion on HN
  7. 7
    Tcpip net sockets faq
    1 people · first shared 2020
  8. 8
    Ui guidelines for async
    1 people · first shared 2017
  9. 9
  10. 10
    Async producer consumer queue 3 more
    1 people · first shared 2014

What HN people wrote when they linked it

jdthedisciple · 2021-09-24
I have been a C# developer for a while as well now and I'm not sure if this is true: "Use of async void in ASP.NET Core applications is ALWAYS bad. " Depending on the context, it can even be recommended to do async void. See Stephen Cleary's brilliant explanation of this: https://blog.stephencleary.com/2012/02/async-and-await.html Edit: The correct link is this, see first table column exceptions: https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/asyn…
MaxBarraclough · 2020-12-14
Disagree, async (edit: in .Net at least) is pretty complex. For instance, from my experience it seems few C# programmers know that it's possible to deadlock if you get it wrong. [0][1] Plenty of people get confused with the basics, too. [2][3] [0] https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html [1] https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/asyn… [2] https://stackoverflow.com/a/34681101/ [3] https://stackoverflow.com/a/34799307/
edgyswingset · 2015-08-21
Yep! The async model in node.js is very similar. Here's a fantastic blog post about the details of it all: http://blog.stephencleary.com/2013/11/there-is-no-thread.html
PathOfEclipse · 2025-05-16
I've been working in .NET/C# for the past few years, and while I'm happy with it, I still think the JVM/Java are the best ecosystem overall I've worked in. It's amazing how many things the Java ecosystem gets right that .NET gets wrong. For instance, Java introduced the fork/join pool for work stealing and recommended it for short-lived tasks that decomposed into smaller tasks. .NET decided to simply add work-stealing to their global thread pool. The result: sync-over-async code, which is the only way to fold an asynchronous library into a synchronous codebase, frequently results in whole-application deadlocks on .NET, and this issue is well-documented: https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html Notice the solution in this blog is "convert all your sync code to async", which can be infeasible for a large existing codebase. There are so many other cases like this that I run into. While there have been many mistakes in the Java ecosystem they've mostly been in the library/framework level so it's easier to move on when people finally realize the dead end. However, when you mess up in the standard library, the runtime, or language, it's very hard to fix, and Java seems to have gotten it more right here than anywhere else.
MarkSweep · 2025-01-28
This is great and despite what the article says, an actually useful technique. The reason this performs well is the .NET runtime does monomorphization when the generic type parameters are value types (primitive types like int or user-defined structs). In modern .NET you would use static abstract methods interface methods to implement this feature. But if you have to run on the old Windows-only .NET Framework, you need to use the approach used in BrainFlood. See this article [1] for more details. An example of an official .NET library that uses this technique on .NET Framework is TensorPrimitives. See this file [2], the structs are defined at the bottom. [1]: https://blog.stephencleary.com/2022/10/modern-csharp-techniques-3-gene… [2]: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Numer…
karl_p · 2019-01-29
Yeah, I should clarify what I was saying. I wasn't calling Task.Run(), but I was doing: for (var i = 0; i < 1000000; i++) { var unused_task_var = RunTaskAsync(); } await RunMainMonitoringTaskAsync(); And I purposefully wasn't calling await on "unused_task_var". And this was doing what I wanted, running all those tasks on multiple background threads in the pool, as long as RunTaskAsync method itself yielded once early on in its function (to return control back to the for loop). tl;dr If you call an async method and don't await on it, it will be parallelized - for a console app, at least. Honestly, I don't find the MS docs on this to be that great. Not 100% sure I'm even doing it the right way here. Everybody says use Task.Run but that is for CPU bound tasks. I want to run a ton of IO waiting tasks. edit: looks like I'm doing it right. See "Async Composition" @ https://blog.stephencleary.com/2012/02/async-and-await.html
marijnz0r · 2024-11-14
Although it seems async/await is based on multiple threads, this is not the case. To learn about this, read this blog post: https://blog.stephencleary.com/2013/11/there-is-no-thread.html Please let me know what you think.
loeg · 2023-04-06
They mean you don't know and can't reason about lock state. To design concurrent systems with good performance and correctness, you need to be able to reason about lock state statically. Here's some random article I found in 30 seconds of googling that seems to go into more depth on the subject: https://blog.stephencleary.com/2013/04/recursive-re-entrant-locks.html
Merad · 2022-03-01
The very short version is that by synchronously blocking on the async method your thread may end up holding a resource (thread context info or the calling thread itself) that the async method needs in order to run its continuations. Here are a couple of more detailed explanations: https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html https://devblogs.microsoft.com/pfxteam/await-and-ui-and-deadlocks-oh-m…
kinjba11 · 2022-02-22
If you don't know what a thread is, start there. Otherwise, you might read this. https://blog.stephencleary.com/2013/11/there-is-no-thread.html
piedar · 2021-09-24
> 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-code.html
dharmaturtle · 2021-08-02
Hey, so my my career path has been C# (many years) -> F# (couple years) -> Clojure (3 months). I understand multithreading primarily through the lens of async/await, and have been having trouble fully grokking the Clojure's multithreading. One of the commandments of async/await is don't block: https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html Which is why the async monad tends to infect everything. Clojure, as far as I can tell so far, doesn't support anything similar to computation expressions. So I'm guessing your "poked at it a couple times" was something like calling `pmap` and/or blocking a future? All my multithreaded Clojure code quickly blocks the thread... and I can't tell if this is idiomatic or if there's a better way.
guu · 2020-04-15
It's .NET focused but this FAQ[0] (2009) has good general advice regarding things like message framing and detecting dropped connections. [0]: https://blog.stephencleary.com/2009/04/tcpip-net-sockets-faq.html
onefuncman · 2019-12-26
There's terrible dangers that await one who casually throws .Result or .Wait() into an existing ASP.NET application. The intersection between synchronous and asynchronous code is the ugliest. The most dangerous is how deadlocks can arise because of the execution context of a running application vs the execution context of your unit tests. If you're already in .NET Core, you're probably fine. For more, try https://devblogs.microsoft.com/pfxteam/should-i-expose-synchronous-wra… or any of Stephen Cleary's blog posts https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
SideburnsOfDoom · 2019-12-17
> Sooner or later, in anything built with a computer, there's going to be polling. ... But under the hood, there's polling. Is that true? I am reminded of this essay that goes into a deep dive of what happens under the hood with http and the underlying network I/O https://blog.stephencleary.com/2013/11/there-is-no-thread.html And at the lowest level, the network card interrupts the CPU because it has finished reading or writing data. > Some time after the write request started, the device finishes writing. It notifies the CPU via an interrupt. Is that polling? It seems more like a push.
StefanKarpinski · 2019-07-24
C# has threads but async/await does not use them. If you think that `async` in C# does the same thing as `@spawn` in Julia and `go` in Go, you are mistaken. It does not. There is also an `@async` macro in Julia that does what the `async` keyword in C# does (which, again, does not involve threads). https://stackoverflow.com/questions/37419572/if-async-await-doesnt-cre… https://blog.stephencleary.com/2013/11/there-is-no-thread.html https://blogs.msdn.microsoft.com/benwilli/2015/09/10/tasks-are-still-n…
me551ah · 2019-01-28
This is a good article which tries to explain how .NET works under the hood. https://blog.stephencleary.com/2013/11/there-is-no-thread.html
jdmichal · 2017-06-13
This is basically "There is no Thread": https://blog.stephencleary.com/2013/11/there-is-no-thread.html There are times when you can offload an asynchronous task to non-CPU hardware, in which case the blocking thread can disappear. The thread reappears when the hardware asynchronously informs the CPU of task completion. (This idea is what Node.JS is entirely reliant upon.) Consider if we didn't have timing hardware to handle this task for us. We could implement a timer with a spin-loop thread which checks the clock and fires off events.
recursive · 2017-01-12
Async/Await does not imply multiple threads. Often, the async version will use fewer threads than the equivalent synchronous code. http://blog.stephencleary.com/2013/11/there-is-no-thread.html
Matthias247 · 2014-11-13
How about BlockingCollection<T>? If I understand it correctly you can even have select like sementics with BlockingCollection.TakeFromAny(...) Edit: Sorry, messed it up a little bit. BlockingCollection will block your current thread for any other computation and does not allow to await on it. But there seem to be alternatives: http://stackoverflow.com/questions/21225361/is-there-anything-like-asy… http://blog.stephencleary.com/2012/12/async-producer-consumer-queue-3-…
Strilanc · 2014-11-07
Because blocking on tasks is a deadlock waiting to happen [0], and creates UI hiccups when the task takes longer than 16ms to complete. In other words, the conditions you must satisfy for using Task.Wait or Task.Result are very complicated. They depend on and create global constraints of the entire program, and so you should avoid using them whenever possible. 0: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
ewjt · 2022-06-01
I’m not picking on you specifically, it sounds like you do a lot of desktop .NET development and there are legitimate complaints there. However, “too invested to switch now” reminds me of .NET/C# developers that don’t embrace change and/or don’t swim in other waters. Some folks with a traditional Microsoft/Windows/.NET Framework mindset have real trouble with these things: >“Windows and Visual Studio” Rider, Linux, Docker, bash terminals, and MacOS are all things that can improve your ability to solve problems with .NET. I’ve met dozens of people that refused to even try a different OS/IDE and fail to gain the benefits of those tools and the broadening effect they have on your perspective. >”too many frameworks” This complaint sometimes stems from an unwillingness to learn web/mobile frontend technologies. On the server-side, ASP.NET continues to simplify— you can write an API now in one file with a handful of lines of code. [1] >“limited core libraries” The first party support in .NET is already pretty comprehensive. Some folks have an aversion to using open source packages, but this is exactly how you get things like YAML parsing in most other languages. Microsoft’s move to open source has been awesome. You can literally interact & contribute with the team’s that make the framework. >”command line first” This sounds like a feature to me. This allows for automation and is easier to document without a series of screenshots. Having a common CLI allows a team…

Keyboard shortcuts

/Search
j kNext / previous blog
EnterOpen selected blog
vVisit the blog site
fFollow / unfollow
lSwitch grid / list view
dSwitch dark mode
g hHome
g pTop posts
g tTopics
g fFollowing
g lLiked
g yHistory
g qFAQ
EscBack / close