6 ms·
What this likely means is for you to take advantage of the underlying runtime multiplexing green threads over multiple physical ones running on multiple cores,
by neonsunset 2y ago
What this likely means is for you to take advantage of the underlying runtime multiplexing green threads over multiple physical ones running on multiple cores, you need to explicitly fork the execution flow.
This could be as simple as a web server firing off a new green thread or a goroutine for an incoming request, or as contrived as doing so manually within a function scope.
In practice, there really is not much difference with async/await. "Green threads" is a combination of implementation details and a subset of what async/await abstractions achieve.
Effectively, Goroutines are in many ways similar to C# Task<T>s. The difference is that in Go you are expected to explicitly send the result via a channel or some other data structure and then synchronize the completion of the execution, where-as with tasks you simply await that.
There could be an argument made about preference of implicit suspend (Go, Java, BEAM family) over explicit suspend (C#/F#, Rust, JS, Python, C++ co_await, Swift), but for practical purposes invoking a function with 'go' keyword in Golang is very similar to firing off a synchronous method with Task.Run in C#, or calling an asynchronous method (with sufficiently short body before first yield) and not immediately awaiting it.
As I usually post it on HN, tasks make the following patterns trivial:
using var http = new HttpClient {
BaseAddress = new("https://news.ycombinator.com/")
};
// not immediately awaited requests are executed in parallel
var frontPage = http.GetStringAsync("news?p=1");
var secondPage = http.GetStringAsync("news?p=2");
Console.WriteLine($"{await frontPage}\n\n{await secondPage}");
- spinningslate 2y ago> The difference is that in Go you are expected to explicitly send the result via a channel or some other data structure and then synchronize the completion of the execution, where-as with tasks you simply await that. That may be be the case in Go but it's not an inherent property of green threads. See, for example, Gleam Tasks [0] which are based on green threads and provide the syntatic convenience of being able to await the result rather than receiving a message: let task = task.async(fn() { do_some_work() }) let value = do_some_other_work() value + task.await(task, 100) They do so without the disadvantage of bifurcating the code base into sync and async functions. [0] https://hexdocs.pm/gleam_otp/gleam/otp/task.html https://hexdocs.pm/gleam_otp/gleam/otp/task.html
- neonsunset 2y agoOf course. The discussion regarding Goroutines is to highlight that, despite prevalent claims of otherwise, they are not doing something unique and for developers who are used to languages with powerful concurrency primitives look like an incomplete task abstraction. "Green Threads" really is an implementation detail, in many ways orthogonal to pros/cons of implicit and explicit suspend points. I hope your opinion about C#'s task system has improved since the last time[0], given what Gleam (and, in many ways, Elixir) does looks practically identical :) [0]: https://news.ycombinator.com/item?id=40427935 https://news.ycombinator.com/item?id=40427935
- spinningslate 2y ago>I hope your opinion about C#'s task system has improved since the last time[0], given what Gleam (and, in many ways, Elixir) does looks practically identical :) Well no, not really I'm afraid. My reservation has always been with the codebase birfircation per my previous post. Gleam/BEAM languages, Go, and now Java, don't have async and sync functions. They have one kind of function which can be called either synchronously or asynchronously. The difference is in who decides: the function caller or function implementer. That a sync function can't call an async one amplifies the problem. I know you dislike the "coloured function" metaphor [0] but for me it's a significant issue. I look at lots of C# and Python code, and see libraries now encumbered with both sync and async function variants (e.g. [1] [2]). That, to me, is a significant downside to async/await as implemented in those languages. The Gleam example has all the convenience and readability of its C#/Python counterpart - but without the downsides. I don't doubt you'll disagree :). [0] https://news.ycombinator.com/item?id=39497110 https://news.ycombinator.com/item?id=39497110 [1] https://learn.microsoft.com/en-us/dotnet/api/system.io.stringreader?view=net-8.0 https://learn.microsoft.com/en-us/dotnet/api/system.io.strin... [2] https://fastapi.tiangolo.com/async/ https://fastapi.tiangolo.com/async/
- neonsunset 2y ago> The Gleam example has all the convenience and readability of its C#/Python counterpart - but without the downsides. https://benchmarksgame-team.pages.debian.net/benchmarksgame/box-plot-summary-charts.html https://benchmarksgame-team.pages.debian.net/benchmarksgame/... :) Assuming it is unlikely Gleam does something that makes it outperform Erlang, which it compiles to. Now, it comes down to just how BEAM VM is, but nonetheless an argument - C# is a close to the metal programming language where your choices directly affect what happens under the hood, including the use of async/await. For some it may be an undesirable trait but it's precisely what makes it so fast, in domains where languages that are "more abstracted away" used to historically struggle and continue to have lower performance ceiling. As usual, the "coloring" point misses the patterns that async/await enables, and that it is in many ways an "I/O Monad". Still, mixing faux-concept "differently colored threads" in .NET does not come with the same degree of pain it does in Rust or elsewhere (and there are good reasons for that). You can block threads if you have to (which includes synchronously waiting for tasks), and Threadpool is desiged to deal with that appropriately and increase/decrease worker threads count to maintain optimal throughput. You just don't have to pay for it always, and as multitude of alternate implementations suggest there is no free lunch, as usual. Also, sync vs non-async overloads, as I previously discussed, often do I/O in a completely different way. Other languages sell it with loud buzzwords like "NIO", while .NET keeps it boring - the workload will scale without your explicit effort, never throttling independent execution flows. I continue to be convinced of the sheer degree of harm done by "that one article", and applying C# in practice cures this perception once you stop worrying and love the easy concurrency and parallelism that come with it.