关闭频道与发送频道 一个空的结构?

I have a pipeline with goroutines connected by channels so that each goroutine will trigger another one until all have run. Put even simpler, imagine two goroutines A and B so that when A is done it should tell B it can run.

It's working fine and I have tried a few variants as I have learnt more about pipelines in Go.

Currently I have a signalling channel

ch := make(chan struct{})
go A(ch)
go B(ch)
...

that B blocks on

func B(ch <-chan struct{}) {
    <-ch
    ...

and A closes when done

func A(ch chan struct{}) {
    defer close(ch)
    ...
}

This works fine and I also tried, instead of closing, sending an empty struct struct{} in A().

Is there any difference between closing the channel or sending the empty struct? Is either way cheaper / faster / better?

Naturally, sending any other type in the channel takes up "some" amount of memory, but how is it with the empty struct? Close is just part of the channel so not "sent" as such even if information is passed between goroutines.

I'm well aware of premature optimization. This is only to understand things, not to optimize anything.

Maybe there's an idiomatic Go way to do this even?

Thanks for any clarification on this!

closing a channel indicates that there will be no more sends on that channel. It's usually preferable, since you would get a panic after that point in the case of an inadvertent send or close (programming error). A close also can signal multiple receivers that there are no more messages, which you can't as easily coordinate by just sending sentinel values.

Naturally, sending any other type in the channel takes up "some" amount of memory, but how is it with the empty struct?

There's no guarantee that it does take any extra memory in an unbuffered channel (it's completely an implementation detail). The send blocks until the receive can proceed.

Close is just part of the channel so not "sent" as such even if information is passed between goroutines.

There's no optimization here, close is simply another type of message that can be sent to a channel.

Each construct has a clear meaning, and you should use the appropriate one.

  • Send a sentinel value if you need to signal one receiver, and keep the channel open to send more values.

  • Close the channel if this is the final message, possibly signal multiple receivers, and it would be an error to send or close again.

You can receive from closed channel by multiple goroutines and they will never block. It's a main advantage. It's one_to_many pattern.

finish := make(chan struct{}) can be used in many_to_one pattern when many concurrent runners want to report things done, and outsider will not panic.

It's not about memory consumption.