Just from observations over the years, I don't think there's any other language quite like this, in terms of how things can end up happening in any thread.
Go Concurrency Distilled (antonz.org)
SamInTheShell 21 hours ago
chimbambum 21 hours ago
unscaled 21 hours ago
Kotlin gets this right. On send/receive, you can use trySend or tryReceive if you want to avoid exceptions. Considering Kotlin also has coroutines and structured concurrency, concurrency in Kotlin feels more ergonomic to me than Go. At least if you want to get concurrent code with least amount of bugs and not just least amount of extra keywords.
Groxx 21 hours ago
That and the lack of tooling around mutex usage / concurrency correctness. The race detector is legitimately excellent and every language needs it, but it can only catch races that you trigger in tests/builds with it enabled, and few projects write anywhere near sufficient concurrent tests to catch issues in practice. There isn't even a "this var claims to be protected by lock X, but it is not held [here]" lint, or "this var is atomic but used non-atomically [here]" (though this one is significantly less of an issue with generics, as safe zero-cost abstractions now exist).
bbkane 19 hours ago
Groxx 18 hours ago
The primitive generic atomics in the stdlib don't run into those details, so you really do get pretty much exactly the compiled code as what you'd write inline by hand:
>In particular, fundamentally different built-in types such as int and float64 are never in the same gcshape. Even int16 and int32 have distinct operations (notably left and right shift), so we don’t put them in the same gcshape.
jerf 21 hours ago
Channels are all intrinsically multi-producer, multi-consumer, and unbounded in size (which is to say, they can carry an indefinite number of messages, not related to channel buffering), but you should still know what the characteristics of your channels are, namely, single or multiple producer and consumer and whether there's some sort of bound on the number of messages. Particularly because you should only ever close a channel if it is single-producer and you are the producer.
It is OK to only use a fraction of a channel's power. For instance, a single-producer, single-consumer channel that is guaranteed (by code, not type system) to only ever have either 0 or 1 messages sent on it is a fairly common pattern.
Never just buffer a channel blindly to try to fix a problem. You should only ever buffer a channel with a size that corresponds to something particular; I know this may receive exactly N messages, 1 from each of N threads, and I want to decouple the possibility the receiver will give up early without hanging the producers, or something like that. Never just slap down a "10" or something and hope it makes things better. The vast majority of channels should be unbuffered.
Putting those two together, the correct way to tear down complicated structures after an error or something is often a channel whose sole purpose is to indicate the liveness of the system in question. In any even remotely modern Go, that should actually be a context.Context and not a channel, which is still basically "a channel with a defined close mechanism" under the hood but adds some other features that are almost always useful at some point.
The reason for all of the above is the select statement. You can in some sense look at "select" as the dual of the channel (being a bit free with the term "dual" here) and consider its functionality as the functionality the Go runtime is actually trying to provide you, from which the characteristics of channels are derived. From this point of view it is then trivially obvious why sends and receives to nil channels block forever... "block forever" is the channel-focused way of seeing the dual statement "the select statement will never select this channel". Some of the other details of channel behavior make more sense if you view them from the select side of the coin.
From this we can also derive a rule of thumb in Go, which is, if your "concurrency" is never going to be involved in a select, it probably doesn't need to be a channel. For example, a simple atomic counter really shouldn't be wrapped behind a channel with a goroutine reading from it or something, just use atomic integers. I have a number of mutexes in my real code. However, never ever take more than one mutex at a time. As soon as you feel like you need to do that, switch to channels, and a proper architecture that uses them somehow to do whatever it is you are trying to do.
(Trying to take multiple mutexes at a time is what led to threading hell in the 1990s. Contrary to popular belief, not just the mere act of threading, but the attempt to do so based on taking multiple mutexes, which at the time was thought to be the only technique available by a lot of the community, leading "threading" to take the heat for what should have been laid at the feet of "taking lots of mutexes at a time in one thread".)
I don't know much about Kotlin, but your cite of "trySend" and "tryReceive" makes it sound like you can do that on only one channel at a time. The fundamental thing about Go channels is that they can be put into select statements which can atomically send from or receive from multiple channels at a time, guaranteed to select exactly one of the possible outcomes. Many "I implemented Go concurrency in X" (often C) flop here. Some kind of queue than can be sent and received on is ubiquitous. Go channels aren't unique, because by the time Go came around pretty much every primitive had been tried somewhere, but it is to my knowledge the only langauge that lifted channels up into the language itself and made them first class.
But stepping up one level of abstraction, "lifting up a particular concurrency primitive to the language level" is not itself anything particularly special and I'm not claiming it is. For instance BEAM had a very particular concept of "mailbox" that it had lifted up into the language and runtime around 15 years earlier, which I have compared and contrasted before here: https://news.ycombinator.com/item?id=34564228 which is, overall, a richer concept than Go's channels, particularly because of its ability to pluck messages out of the mailbox out of the receiving order. Whether that richness is a good thing is something that could be debated a lot.
urxvtcd 11 hours ago
_flux 2 hours ago
Concurrent ML had something like that, and it was implemented in OCaml as well: https://ocaml.org/manual/5.5/api/Event.html
And I would say it even goes further than Go by making the actual events first class (no need for language support for this either): send, write and choose (aka select) are also events, so you can build your own things you can select on. Select would be basically defined as
let select events = sync (choose events)
so sync is the way to convert events to values (and blocking in the progress).CML had one extra trick in its sleeve: it was able to garbage collect threads that were not able to proceed. I'm not aware of any other system that can do that. This would e.g. resolve leaking coroutines in Go, at least in some situations..
saghm 18 hours ago
Not only that, but writing to a closed channel also panics. You need to close it exactly once from the sender side, and then somehow differentiate on the receiving side between an explicit zero value being sent, the channel being empty but not closed, and the channel being empty but not closed. It's not clear to me how this is possible without either using another channel (and then basically repeat the same problem on that new channel) or use some sort of shared memory like an atomic bool, at which you're no longer purely message passing.
I don't have any qualms with shared atomic primitives for synchronizing concurrency, but it's kind of weird that everyone talks so much about goroutines and channels when channels have such a weird design. Needing to use a separate mechanism to circumvent completely avoidable design issues for anything more complex than "never close the channel" does not seem particularly praiseworthy to me.
prerok 13 hours ago
Of course, that won't work if you want to receive from several channels in the same goroutine. For that you can use select with receive assigning to two values and the second one is set to false if channel is closed.
So, I never really had issues on receive side, I agree with the send side, though. The solution I used when sending from multiple goroutines is to wait for the senders to be finished in the "main" goroutine and only the close the channels.
But yeah, it does require some thought to be put into how this is all organized.
saghm 8 hours ago
How do you know when they're finished though? It seems like you're need to have an additional channel or atomic boolean per goroutine for this, which just increases the amount of organizational burden.
prerok 7 hours ago
Joel_Mckay 21 hours ago
In production, Go has proven solid for several years. It is best when used with the native code people ported.
There are only two issues I encountered:
1. getting the legacy ancient C source meta-circular Go compiler working to port the Go boot-strap compiler upgrade chain is a kick in the pants. However, once it is on a architecture it has proven rather resilient.
2. memory limited systems can develop reliability issues, as Go programs will often ungracefully throw hard to diagnose unrelated errors during each crash. A good metric is 3:1 of your average load as a safety margin (if you see 2GiB in average RAM use, make sure to over-provision the host with 8GiB RAM etc.)
Other than the above short list of edge cases, if you join a pure Go project it is usually pretty reliable. Most community folks interested in the language seem fairly competent at building stuff that is fun. =3
SamInTheShell 17 hours ago
It was literally enabling a flag and running some commands to do some really quick exports.
Dealing with the JVM though, heap dumps are slow to process and the UI I had to download was very clunky. I don't know if there are better tools, but even if there are the path to just doing it isn't straight forward.
Joel_Mckay 16 hours ago
gf000 12 hours ago
Joel_Mckay 11 hours ago
Rule #23: Don't compete to be at the bottom, as you just might actually win.
za3faran 4 hours ago
And let's not kid ourselves, Rust and golang are OOP (minus inheritance).
za3faran 4 hours ago
ehe78qhe 17 hours ago
in this economy?
Joel_Mckay 16 hours ago
eddythompson80 19 hours ago
In my experience the main hurdle was getting developers on the team onboard with go’s way. It felt like swimming upstream for my 6 year stint in go. I was in a very Java heavy “enterprise” but we were writing a kubernetes operator and I pushed to use golang because (a) I liked it, and (b) it was 2019 and the entire kubernetes ecosystem was primarily go.
To me golang was very simple and I drank Rob Pike’s and Google’s narrative of how easy it’s to get a competent “compute science major in college”-person to pick up go. What I experienced was a form of “you can’t teach an old dog new tricks”. Lazy (and I hate to use this word) developers who gotten so used to frameworks and IDEs doing all the heavy lifting for them in Java or C# had 0 appetite forgetting all the questionable patterns they learned over the years and adopt Go’s simplicity. It was very frustrating at time, yet gave me a good eye for the actual skilled talent in the organization vs the average enterprise developer persona.
gf000 10 hours ago
It's almost like those frameworks then achieved their job. Why do you assume you can write better code than what was iteratively refined over years, especially when it's usually not even directly related to any kind of business goal you may have?
synthc 8 hours ago
Sure, not using a framework is nice for a small and simple service.
When you have multiple devs working on a large codebase over the timespan of years, a 'heavy' framework is highly prefereable over everyone reinventing the wheel.
za3faran 17 hours ago
SamInTheShell 16 hours ago
But Go itself comes with it's own runtime built into the final binary. It doesn't work well in some use-cases, I mentioned some of the draw backs in a couple other comments if you want to dig those up.
Also I saw some of the other comments. Channels are ultimately just used for message passing and aren't that complicated. You also can use mutexes or some other locking pattern. There's some primitive atomic structures available that solve some use-cases preventing you from even have to having to really deal with working between goroutines.
kccqzy 21 hours ago
You can implement channels and select using STM, so these don’t have to be in the standard library. And the contentious design choices like what happens when you close the channel twice can be your choice! And going from STM to managing mutexes is a definite downgrade in abstraction power.
The concurrency design in Haskell feels like true magic.
tombert 20 hours ago
It's not because the language is "hard". I remember when I first learned Haskell a million years ago I thought it was the coolest thing ever because I had never seen anyone work at that abstract of a level before, especially in a compiled language. I got to understand the theory well enough and I know how to write a program with it, but the entire language kind of feels slapped together to me. Every time I've written anything in Haskell, I feel like I have to do a million compiler extensions, or rely on third party libraries' liberal use of Template Haskell (e.g. Lens) to make the language feel anywhere near "modern".
Yes yes yes, I know this is a complaint about GHC, not "Haskell", but given that GHC is basically the only Haskell compiler that gets serious use I don't think it's weird to conflate the compiler and the language.
kccqzy 19 hours ago
tombert 19 hours ago
C++ also has a lot of really cool features but I also do not enjoy writing it, actually for similar reasons as Haskell (though I don't think Haskell is nearly as irritating as C++).
> of course hand-writing lenses is just one line anyways
The lenses themselves aren't hard to write; I was referring to the annoying quirk of Haskell where records couldn't have the same field names. Lens has a nice helper macro `makeFields` so that you could more or less automatically have the generated lenses have the clashing names.
To be fair it actually always worked fine for me but it always felt janky until `DuplicateRecordFields` was released.
saghm 18 hours ago
The slogan "avoid success at all costs" definitely is accurate for Haskell
tombert 2 hours ago
"Avoid success at all costs" has always meant (in my mind) to mean "we will prioritize doing things the 'right' way instead of doing things to appeal to corporations". That's fine, I'm all for doing things correctly, but I think a lot of Haskell's bullshit isn't because of that.
It's a common complaint but it's common for a reason: the fact that records couldn't contain the same field names was really stupid. Apparently in the 90's the Haskell devs couldn't fathom two different types both having a field called "ID" or "name". How does allowing multiple objects to have an overlapping field name affect purity? Plenty of other languages, some of which are even more mathy than Haskell, have managed to pull this off (e.g. TLA+). Could it be because records/structs are really just a shitty hack around tuples tacked onto the language? Yes, Lens fixed hat particular problem with Template Haskell, and now there's yet another GHC extension to more or less work around it, but doesn't change the fact that I think it was something actively bad and it wasn't because of "purity" reasons.
tome 2 hours ago
I think it's simply that making field names become functions that select from the record was a simple design that worked, and didn't require anything new to be added to the language.
saghm an hour ago
osigurdson 21 hours ago
Doesn't that describe pretty much any green thread style concurrency implementation.
dlisboa 19 hours ago
Other languages and their implementations of green threads usually have cooperative scheduling or M:1 mapping
dwattttt 19 hours ago
dlisboa 19 hours ago
dwattttt 18 hours ago
falserum 15 hours ago
LtWorf 14 hours ago
gf000 10 hours ago
jeremyjh 7 hours ago
Those are the only mature languages that have all of these features.
gf000 2 hours ago
jeremyjh 8 hours ago
A "hyperthread" can schedule work for two OS threads simultaneously on a single core. An M:N scheduler will schedule millions of green threads on as many cores/hardware threads as you give it (typically you'd give it all of them).
za3faran 17 hours ago
ackfoobar 16 hours ago
AFAIK, in the current implementation, Java's virtual threads yields only when they block (cooperative). But the spec allows a JVM to implement them as preemptive.
gf000 10 hours ago
seabrookmx 16 hours ago
serbuvlad 13 hours ago
foldr 12 hours ago
ncruces 11 hours ago
Also platforms like Wasm still do Mx1 scheduling without async preemption, where Gosched is required at places.
E.g.: my "transpiled" SQLite driver takes special care to make sure long running SQL queries (and the busy handler) can be canceled with contexts even on platforms without async preemption.
smw 8 hours ago
seabrookmx 7 hours ago
nh2 11 hours ago
I believe Go didn't originally have it, and added it in 2020, 14 years after Haskell.
jeremyjh 8 hours ago
dlisboa 7 hours ago
signa11 19 hours ago
hank1931 18 hours ago
Supposedly WhatsApp scaled to serving over 1 billion users with Erlang and BEAM.
RabbitMQ, used by Reddit, uses Erlang and BEAM.
Discord uses Elixer and BEAM.
I just traveled down the BEAM rabbit hole. Fascinating story. The Ericsson Computer Science Laboratory cranked out some amazing products in the early 1990's.
Their goal was five nines of reliability for Ericsson telephone switches.
According to Joe Armstrong (an interesting fellow from Ericsson), the AXD301 ATM switch achieved nine nines over a nine-month period using Erlang and BEAM in 2002. That calculates out to 24 milliseconds of downtime.
SamInTheShell 18 hours ago
nesarkvechnep 15 hours ago
Joel_Mckay 11 hours ago
https://blog.stenmans.org/theBeamBook/#_concurrency_parallel...
SamInTheShell an hour ago
jeremyjh 8 hours ago
One advantage BEAM had for a long time is preemption is built into the VM and based on reductions. Go didn't have true preemption until 1.14 (before that it could only preempt at function boundaries) and its a very complicated implementation based on async signals sent from a runtime thread.
CoolestBeans 17 hours ago
winwang 17 hours ago
CoolestBeans 15 hours ago
Of course, in some sense fault tolerance and scaling are two sides of the same coin. They're both measures of availability. They're just different approaches to that.
What Go achieves that Erlang doesn't is the ability to "pick up and play". What Erlang achieves that Go doesn't is a pathological commitment to the system whole never going down.
jcgl 12 hours ago
Once a sufficiently good proposal was made, generics were adopted.
jasonwatkinspdx 4 hours ago
throwaway894345 9 hours ago
On the other hand, Erlang has been out for ages and has largely failed to attract much adoption, so it doesn’t seem like the market finds it to be “optimal” either. Not that popularity is everything, but over time a language better languages should increase their market share, especially if your language got its start during an era where the competition was C and C++ and Java.
> And I know it's an old thing, but the fact that Go was once adamantly against generics...
Erlang not only lacks generics, but it lacks any static type system at all…
OoooooooO 5 hours ago
And most software does apparently not need what it offers.
winwang an hour ago
If one wanted, they could put ecosystem tooling here as well (e.g. `gofmt` saving everyone time and mental health).
Also, to clarify, I'm not arguing that Erlang > Go, I don't (purposefully) use either, though I have to read Go sometimes.
thayne 3 hours ago
sevenzero 15 hours ago
innocentoldguy 15 hours ago
Having learned both Go and Elixir, I found Elixir easier to learn and a lot more enjoyable to work with. I'm not alone in this opinion. According to Stack Overflow's 2025 "admired" languages, Elixir scored 65.9% compared with Go's 56.5%; Phoenix was the most admired web framework of 2025 at 79% and has held that spot for the past three years.
sevenzero 15 hours ago
lenkite 15 hours ago
whizzter 13 hours ago
In practice, this is why many such languages have JIT's (unless targeting a subset or an type-information enhanced superset like TS), there was a seminal OOPSLA paper in 1995 by Agesen and Hölsze (who worked on the JVM Hotspot compiler) that compared JIT's to AOT compilation in practice (the Agesen CPA algorithm isn't perfect but it's pretty good for the time and others have probed that it's an undecidable problem).
That said, they also had a historically bad performance story due to misjudgments in development direction, the interpreter was default and they twice tried to make "HPC JIT's", ie.. complex JIT's that tried to be "perfect" and focused on numerical code gains, they'd be good for optimizing a matrix kernel, yet fairly useless or even negative on more common code patterns.
OTP 24,25 and 26 took learnings from the JS runtimes and also added compiler hints (since they already had binary precompiled modules).
What still saves the OTP runtime is that many basic operations that would suck without a good performance story is handled by built-in functions, so like Python most practical programs works well enough even if the runtime is behind.
gf000 13 hours ago
smw 8 hours ago
za3faran 4 hours ago
This poster summarized it well: https://news.ycombinator.com/item?id=49864334
chickensong 15 hours ago
Hello, Mike.
Hello, Joe.
System working?
Seems to be.
Okay. Fine.
Okay.drekipus 14 hours ago
kortilla 15 hours ago
This is misleading. I had an old Dell computer in my garage hosting a php app that hit that level of uptime as well over a 9 month period. It was 100% so actually better.
Those uptime numbers only hold water when spread over many thousands to millions of users where you’re at large enough scale that you’re actually dealing with a meaningful volume of hardware failures.
equill 15 hours ago
How many do you serve?
Edit: either you are a troll or you have an affinity for hateposting on HN. Nothing positive have come from your comments.
asp_hornet 13 hours ago
I think Go and the BEAM family languages are both great.
fzeindl 11 hours ago
Truly something else if error handling is built into the language as a default case, not an … exception.
liampulles 9 hours ago
za3faran 18 hours ago
Not to mention golang is not memory safe: https://www.ralfj.de/blog/2025/07/24/memory-safety.html
SamInTheShell 17 hours ago
It's not great for everything and neither is Go. You can find a bit more context on that in some of the other threads.
LtWorf 14 hours ago
Ygg2 10 hours ago
It's coming from Go. In presence of data races on interfaces, slices or maps your memory might get corrupted.
> Also why can't I have my memory back when it's not in use in tightly packed systems?
You can. You have to either set your GC to be more aggressive or you need to utilize value types more.
SamInTheShell 2 hours ago
Regarding the JVM and GC. Good luck with that? Every Java application I've seen in the wild when I supported JVM seemed to never release any ram it allocated. Ever. If it used 1G and even after free, the JVM decided that was going to be used again and wouldn't release it.
It would be hard to sell me on wanting to use Java again (people can pay me enough to do it, but I hate it). Which kinda sucks since Apache Foundation has a ton of really cool projects using it. Kotlin maybe, but I have no real use cases where it would be better than anything else I know right now.
za3faran an hour ago
[1] https://openjdk.org/jeps/346
[2] https://openjdk.org/jeps/546
gf000 10 hours ago
Such as? It's one of the most widely used platform for backend services, basically almost all top 500 company has some business critical infrastructure running Java. It surely can't have "too horrid" problems..
jeremyjh 7 hours ago
SamInTheShell 2 hours ago
za3faran an hour ago
Furthermore, collection time (for moving collectors) is a function of the liveset, not the # of dead objects.
za3faran 4 hours ago
That is changing as we speak:
[1] https://openjdk.org/jeps/8359211
jeremyjh 7 hours ago
gf000 2 hours ago
Quite obviously the meaningful distinction is from manually inserted preempt points, like async/await languages.
Jtsummers 2 hours ago
They fixed that in Go a while ago.
ackfoobar 16 hours ago
Then they added structured concurrency, which roughly solves the same problem as Go's context, arguably more elegantly.
h4x0rr 15 hours ago
slowhorse 13 hours ago
pjmlp 13 hours ago
apatheticonion 12 hours ago
I have tried but I honestly can't go back to Go now, it's so much harder
gf000 11 hours ago
treyd 8 hours ago
tomjen3 12 hours ago
Erlang has had both for over 20 years; it has also had green threads for equally as long—something I don't know if Golang has. I'm sure Golang cannot split itself to run on multiple machines with its actor model. Erlang can.
Jtsummers 3 hours ago
This is in contrast to, say, Erlang which is closer to actors than CSP, with its named processes (actors) and their mailboxes and no channels (though you can use a process as a channel).
nh2 10 hours ago
I also find Haskell's concurrency in practice much easier to reason about than Go's, let me do a pitch:
In Haskell you can just fork a thread and block till it's done. Threaded, "async" logic just looks like blocking serial code (but isn't blocking). I feel like in typical channel-based Go code I have to jump and scroll a lot in the code because of all the message-passing instead of block-scoped "blocking-style" variable use, and that this makes it hard to conclude whether the whole thing terminates or deadlocks.
In Haskell, channels are considered low-level concurrency primitives you should only use when you have no clean high-level primitives for it. This is because they are not "structured" concurrency: When you send something into a channel, it is gone out of your scope, and you now need to track in your brain where it is, and who should consume that thing in the right way ("message-passing").
For example, in Stolon, a high-availability Postgres orchestrator written in Go (https://github.com/sorintlab/stolon), I found the logic for failover with multiple channels and various timeouts very difficult to reason about when investigating failover bugs. I'm pretty sure that would read much easier in Haskell (see below how).
In Haskell, you can start 2, or N, things in parallel, and easily wait till they are done. You can invoke parallel `map` easily.
results <- mapConcurrently f mylist
If f throws on any element, the whole map throws, and other threads get cancelled automatically as expected.You can get bounded, steaming parallelism, easily.
You can set time limits to function calls writing
timeout 1000 (myIoFunction ...)
You can cancel any thread or computation, at any time. The same timeout function can cancel blocking IO operations, such as reading from the terminal or sockets, without having pass around `Context` objects like in Go (which, if you forget it, just makes things hang or deadlock).You wrap the 2 words "timeout 1000" around your function and done.
Concurrency _composes_ in Haskell. You can write
res :: Maybe (Maybe a) < timeout a (timeout b (myIoFunction ...))
and the returned type tells you cleanly at which level the cancellation occured (no mixing into the same `error` type.You can build trees of parallel operations that live and die together.
And there are no data races (because mutability is a very explicit thing), and I'm not even mentioning STM here (which allows you to do database-style transactions across variables) because that's already pointed out in another post.
As a composed example, in Haskell you can write:
timeout 1000 (race (downloadUrl ...) (forever (putStrLn "Still loading ...")))
and that will do exactly what you think it should, with correct Ctrl+C cancellability, and good developer ergonomics.If you enjoy concurrency, give Haskell a shot!
lukaslalinsky 10 hours ago
delifue 10 hours ago
In Rust when receivers all drop, the producer will error instead of blocking, so Rust is better in this aspect.
throwaway894345 9 hours ago
Isn’t this also true of threads? I know you can usually cancel them from a thread handle, but that kills the thread ~immediately without cleaning anything up, right? Presumably you pretty much always want cooperative cancellation?
lukaslalinsky 9 hours ago
jeremyjh 8 hours ago
kccqzy 7 hours ago
SamInTheShell an hour ago
I don't feel this is hacky or even a work around. Just different promises on what goroutines are vs threads/concurrency/processes in other languages.
Exit and panics are promised at the process level in Go.
FacelessJim 9 hours ago
tym0 9 hours ago
Stick to err/wait group and go routines and it's OK. Any PR with a channel or mutex I'll assume the author made a mistake.
lolpython 9 hours ago
osigurdson 8 hours ago
treyd 8 hours ago
osigurdson 2 hours ago
OoooooooO 5 hours ago
Pony with actor model.
Erlang/Elixier on BEAM VM.
BoingBoomTschak 4 hours ago
librasteve 2 hours ago
https://bil-lang.org aims to address this gap … I wrote a post about Bil’s adjustments to Go here https://bil-lang.org/blog/rethinking-classical-concurrency-p...
voidfunc 17 hours ago
Too many years of Java and managing Threads and Runnables probably rotted my brain.
za3faran 17 hours ago
SamInTheShell 17 hours ago
jatins 16 hours ago
superior how -- What does it do better over Go channels in your opinion?
lenkite 14 hours ago
catlifeonmars 14 hours ago
cbg0 13 hours ago
If you only need to launch work and wait for completion, Go 1.25 has sync.WaitGroup.Go -> wg.Go(f) -> by wg.Wait(). No channels like the page says.
Mawr 12 hours ago
Just how Go adding generics to the language didn't magically fix the billions lines of non-generic Go code, adding virtual threads to Java didn't update its entire ecosystem to take advantage of them.
Meanwhile, the entire Go ecosystem from the beginning took advantage of goroutines, so all code you'll ever interact with will have excellent support for them.
gf000 11 hours ago
Also, what 'party'? There is java, go, Haskell and erlang with anything similar. The majority of programming languages don't have such a feature so it's pretty questionable use of word to "be late".
joe_mwangi 3 hours ago
za3faran an hour ago
spring.threads.virtual.enabled=true
We also see library implementations switching to them.vrosas 17 hours ago
mugul 14 hours ago
One question though: your advice is to write things serially first before moving to concurrency, which for me is general programming common sense, but would you argue that once you start writing concurrent code then channels are not well suited compared to "good old" sync primitives (mutexes, etc.)?
vrosas 8 hours ago
goodpaul6 2 hours ago
silisili 14 hours ago
I always ask/tell people to write without channels, and only add them when you have justification for doing so. That leads to much more sane code.
One pattern I see often because random blogs mention it is starting X long lived goroutines, then passing them data via channels, then receiving responses via channels, then handling. In my experience, it's 100x less error prone to just use a semaphore to start a goroutine per data, and have them do their own handling. No channels involved.
cejast 13 hours ago
gandreani 12 hours ago
I've started using golang last year and I feel like I'm missing exactly this kind of experience with these patterns
Mawr 12 hours ago
melodyogonna 11 hours ago
Spamming them all over the place is a red flag imo
amomchilov 8 hours ago
pjmlp 13 hours ago
liampulles 9 hours ago
eikenberry 2 hours ago
kccqzy 19 hours ago
dzogchen 6 hours ago
awfm9 3 hours ago
jeffrallen 7 hours ago
Goroutine leaks in prod are no laughing matter. They are difficult to debug without killing the process, and that's only useful if you are sure you're going to get stderr to get the full traces of all goroutines.
Jeeetendra 10 hours ago
kccqzy 3 hours ago
Segv77 18 hours ago
fizlebit 13 hours ago
gandreani 12 hours ago
rienbdj 14 hours ago
mrkeen 13 hours ago
masklinn 10 hours ago
A channel of size 1 is a bit like an mvar but with support for only take and put.
phplovesong 16 hours ago