V
V
Vitaly Yakovenko2014-12-11 21:59:05
go
Vitaly Yakovenko, 2014-12-11 21:59:05

Special magic with channels in golang?

Take a look here gentlemen.
I have 30 goroutines competing to be the first to send their number to the channel.
The judge goroutine takes the first number from the channel and closes it, because he did his task, and we are not interested in the numbers from the losing gorutins.
And now the crux of the matter.
First, why can't I make the channel buffered (with a buffer size of 1)? For example, with the goal of immediately launching something important in the winning routine, and not waiting until the referee goroutine deigns to pull out the number?
Secondly, what happens if you close the buffered channel before reading from it all the values ​​that have managed to get there? Will such a chan be collected by the garbage collector when it goes out of scope? What will happen to the objects inside the channel?
Let there be dispute.

Answer the question

In order to leave comments, you need to log in

2 answer(s)
T
Tyranron, 2014-12-11
@Xazzzi

Let me answer the second question first.
close() has nothing to do with scope at all, it just makes the channel "closed", that is, such that when reading values ​​from the channel and writing from it, a panic occurs. It's all. Respectively...

what happens if you close the buffered channel before reading from it all the values ​​that have managed to get there?
The channel will close, the next time you try to read a value from it, you will get a panic. The values ​​that remained inside, consider lost, you will never get them again.
UPD: It's not! (see end of post and comments)
Will such a chan be collected by the garbage collector when it goes out of scope? What will happen to the objects inside the channel?
It will be collected by the garbage collector only when no one else refers to it (if it is declared locally, then yes, out of scope). Objects inside will also be collected if no one else refers to them. Those that are still referenced by someone will remain.
Now for your example:
select {
case b <- number:
    fmt.Printf("Sent into b: %d\n", number)
default:
    fmt.Printf("b chan closed\n")
}
This piece is very misinforming. Firstly, select to write with a default section in no way saves from panic when writing to a private channel. It just makes writing to the channel always non-blocking. As soon as you try to write something into a closed channel with such a select, you will immediately panic. Therefore, correctly for perception, this place looks like this:
select {
case b <- number:
    fmt.Printf("Sent into b: %d\n", number)
default:
    fmt.Printf("Number %d just has been thrown away\n", number)
}
If you make channel a buffered, then your panics will fly to you, because you are writing to a closed channel.
A logical question: why then panic'i do not fly with an unbuffered channel a?
Answer: You're just lucky =)
First, on the playground runtime.GOMAXPROC=1 and runtime.NumCPU=1. That is, in reality, the whole thing revolves in one thread and the parallelism here is pseudo.
Secondly, locally (OS X) this script panics me after receiving the number 25 even with runtime.GOMAXPROC=1. You are just lucky that the playground's internal goroutine scheduler behaves in exactly the same way. With a buffered channel, it behaves a little differently and you get a regular panic immediately.
If completely on the fingers on the first question, then:
With an unbuffered channel, close (b) for some reason of the scheduler is executed only after both goroutines have completed, if you look at the output, then the inscription "B: 1" will be at the very end. That's why everything works fine, although this behavior is not at all guaranteed by the program logic, on the contrary, the program is designed for close (b) to be executed immediately after we read something from there. This is what happens if channel a is made buffered (the inscription "B: 1" flies out immediately), since in this case the scheduler changes its mind, and we get a regular panic.
Additionally:
Channel b must be buffered, otherwise, if the judge goroutine runs faster than the main goroutine starts listening to the channel at all, then all values ​​will simply be thrown out.this article in the last two paragraphs.
I made a mistake, as Vitaliy Yakovenko and SilentFl rightly pointed out in the comments , a closed channel does not panic when reading from it. It allows you to read all the values ​​that are left in it, after which it gives "empty" values ​​for its type, never again blocking execution.
A private channel only panics when you try to send a value to it.

U
uvelichitel, 2014-12-12
@uvelichitel

The judge goroutine takes the first number from the channel and closes it, because he did his task, and we are not interested in the numbers from the losing gorutins.
It is ideal to close the channel to the sending side.
If you don't need a send but don't want to block the channel, then _=<-chan.
And now the crux of the matter.
Firstly. You can.
Secondly. From the closed channel, you can read all the messages and then read zero value from it for an arbitrarily long time. This property is exploited for the broadcast message.
sync.Once is certainly the correct solution, but it reduces the generality to a single winner. You can only get by with channel play.golang.org/p/T8k8_97Iqh

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question