←back to thread

Go channels are bad

(www.jtolds.com)
298 points jtolds | 2 comments | | HN request time: 0.41s | source
Show context
hacknat ◴[] No.11211002[source]
I think I've just come to accept that sychronization is the pain point in any language. It's callbacks, promises, and the single event loop in nodejs. It's channels in golang.

No one can come up with a single abstraction for synchronization without it failing in some regard. I code in go quite a bit and I just try to avoid synchronization like the plague. Are there gripes I have with the language? Sure, CS theory states that a thread safe hash table can perform just about as well as a none-thread safe, so why don't we have one in go? However...

Coming up with a valid case where a language's synchronization primitive fails and then flaming it as an anti-pattern (for the clicks and the attention, I presume) is trolling and stupid.

replies(3): >>11211077 #>>11211292 #>>11211863 #
yetihehe ◴[] No.11211077[source]
> No one can come up with a single abstraction for synchronization without it failing in some regard.

Erlang did. Or at least it's as close as possible.

replies(2): >>11211145 #>>11211260 #
jerf ◴[] No.11211260[source]
I've been bitten by the fact that Erlang lacks a channel-like primitive. You've got half-a-dozen "pool" abstractions on github because it's actually sorta hard to run a pool on pure asynchronous messages when there is absolutely no way to send a message out to "somebody", the way Go channels can have multiple listeners. I know that would only work on a local node but there's already a couple of functions that have already penetrated that abstraction anyhow.

You also have to deal with mailboxes filling up, still have problems with single processes becoming bottlenecks, and the whole system is pervasively dynamically typed which is fine until it isn't.

It is pretty good, but it's not the best possible. (Neither is Go. I still like Erlang's default of async messages better in a lot of ways. I wish there was a way to get synchronous messages to multiple possible listeners somehow in Erlang, but I still think async is the better default.)

replies(1): >>11211606 #
yetihehe ◴[] No.11211606[source]
> You've got half-a-dozen "pool" abstractions on github because it's actually sorta hard to run a pool on pure asynchronous messages when there is absolutely no way to send a message out to "somebody"

You can store receivers in ets table and implement any type of selection algorithm you want or have some process which selects workers. There is no default method, because one default method is not good for everyone and people will complain that it's not good for them. Implementing pools is easy in erlang, I've done tailored implementations for several projects.

> You also have to deal with mailboxes filling up

Yeah, unless you implement back-pressure mechanism like waiting for confirmation of receiving. In ALL systems you have to deal with filling queues.

> I wish there was a way to get synchronous messages to multiple possible listeners somehow in Erlang

You can implement receiver which waits for messages and exits when all are received or after timeout, it's trivial in erlang but I haven't needed it yet. Here is a simple example:

    receive_multi(Acc,0) ->
        Acc;
    receive_multi(Acc,Num) ->
        receive {special,Data} ->
            receive_multi([Data|Acc],Num-1)
        after 5000 ->
            Acc
        end.
replies(2): >>11211833 #>>11212236 #
jerf ◴[] No.11211833[source]
"You can store receivers in ets table and implement any type of selection algorithm you want or have some process which selects workers."

Your process that selects workers has no mechanism for telling which are already busy.

It is easy to implement a pool in Erlang where you may accidentally select a busy worker when there's a free one available. Unfortunately, due to the nature of the network and the way computations work at scale, that's actually worse than it sounds; if one of the pool members gets tied up, legitimately or otherwise, in a long request, it will keep getting requests that it ignores until done, unnecessarily upping the latency of those other requests, possibly past the tolerance of the rest of the system.

"You can implement receiver which waits for messages and exits when all are received or after timeout, it's trivial in erlang but I haven't needed it yet."

That's the opposite of the direction I was talking about. You can't turn that around trivially. You can fling N messages out to N listeners, you can fling a message out to what always boils down to a random selection of N listeners (any attempt to be more clever requires coordination which requires creating a one-process bottleneck), but there is no way to say "Here's a message, let the first one of these N processes that gets to it take it".

You wouldn't have so many pool implementations if they weren't trying to get around this problem. It would actually be relatively easy to solve in the runtime but you can't bodge it in at the Erlang level; you simply lack the necessary primitives.

replies(2): >>11212031 #>>11213639 #
1. yetihehe ◴[] No.11212031[source]
Then it's even easier, pool selector just hands out free workers and deletes them from queue. When worker is free, it just sends a message "I'm free" and it gets added to "free" pool. Yes, it will be "one master process is a choke point" but it's only a problem when your tasks are so short that sending messages is slower than doing the work. But then probably sending messages is the wrong way to do those tasks. There are so many pool implementations because there are many possible solutions depending on what exact problem you have.
replies(1): >>11212177 #
2. jerf ◴[] No.11212177[source]
"Yes, it will be "one master process is a choke point" but it's only a problem when your tasks are so short that sending messages is slower than doing the work."

You're simply reiterating my point now, while still sounding like you think you're disagreeing. Yes, if you drop some of the requirements, the problem gets a lot easier. Unfortunately these are not such bizarre requirements, and Erlang tends to be positioned in exactly the spaces where they are most likely to come up.

"But then probably sending messages is the wrong way to do those tasks."

That translates to "Erlang is the wrong solution if that's your problem". Since my entire point all along here has been that Erlang is not the magic silver bullet, that's not a big problem for me.