Skip to content
HomeVideosBooksBlogs
AllTop postsTopics
Podcasts
HistoryListsWatch laterLiked videosFollowing

The videos, books, blogs and podcasts Hacker News keeps recommending, all in one place.

VideosBooksBlogsPodcastsAboutPrivacySettings

Data from Hacker News, HackerTube, HackerBooks, HackerBlogs and Open Library. Credits · Not affiliated with Y Combinator.

HomeVideosBooksBlogsPodcasts
Ctrl K
    S

    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.

    Visit blogHN submissions
    ProgrammingLanguages & CompilersSystems & Performance
    What HN says

    HN commenters call his async posts brilliant and cite them for explaining async void, deadlocks, and why there is no thread in async.

    Stats for nerds
    29HN people shared it
    7linked in comments
    7story points
    May 2025last shared
    2014people per year2025

    Top posts on HN

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

    What HN people wrote when they linked it

    • ewjtJun 2022

      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…

    • PathOfEclipseMay 2025

      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.

    • karl_pJan 2019

      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

    • MarkSweepJan 2025

      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-generic-code-generation.html

      [2]: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netstandard/TensorPrimitives.Single.netstandard.cs

    • piedarSep 2021

      > 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

    • dharmaturtleAug 2021

      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.

    Related

    John Regehrblog.regehr.org401 people

    Teaching C

    John Regehr
    Personal · last shared 2026
    Concurrency Freaksconcurrencyfreaks.blogspot.com26 people

    Why is memory reclamation so important for lock-free algorithms?

    Concurrency Freaks
    Personal · last shared 2026
    Herb Suttergotw.ca84 people

    Ritchie, Stroustrup, and Gosling interview

    Herb Sutter
    Personal · last shared 2026
    Nathaniel J. Smithvorpus.org123 people

    Why does calloc exist?

    Nathaniel J. Smith
    Personal · last shared 2026
    Overreactedoverreacted.io186 people

    Goodbye, Clean Code

    Overreacted
    Personal · last shared 2026
    notgullnotgull.net20 people

    Why choose async/await over threads?

    notgull
    Personal · last shared 2026
    Andrew Lockandrewlock.net47 people

    Understanding the Worst .NET Vulnerability

    Andrew Lock
    Personal · last shared 2026
    Stephen Brennanbrennan.io37 people

    Write a Shell in C (2015)

    Stephen Brennan
    Personal · last shared 2026