-
Главная запись
Account создан для того, чтобы читать интересных мне авторов. Ну и иногда записывать разные интересные вещи.
By logging in to LiveJournal using a third-party service you accept LiveJournal's User agreement
func foo(j int) {
fmt.Printf("running #%v\n", j)
}
func main() {
for i := 0; i < 5; i++ {
go foo(i) // foo is a regular func that we just choose
// to run in async mode here with go keyword
}
}
foo
actually “returning” something to the caller:var ch = make(chan int) // Make the channel: chan keyword.
func foo(j int) {
fmt.Printf("running #%v\n", j)
ch <- j // Send on the channel
}
func main() {
for i := 0; i < 5; i++ { // This stays the same as before.
go foo(i)
}
for j := 0; j < 5; j++ {
res := <- ch // Receive all the results, in
// no particular/deterministic order
fmt.Printf("result %v\n", res)
}
}
It is intuitively clear how to send and receive things on a channel. Both send and receive are blocking operations by default. Specifically, sender blocks until there is a receiver “on the other side” ready to read from the channel.var ch = make(chan bool, 3) // This is a buffered channel; 3 is the buffer size.
func foo(j int) {
fmt.Printf("running #%v\n", j)
<-ch // Ignore the value read from the channel. The point is to “unload” one value from the buffer.
// Actual value does not matter here: send and receive are only used for synchronization.
}
func main() {
for i := 0; i < 5; i++ {
ch <- true // First 3 send operations on the channel are non-blocking b/c of the buffer size.
// So the first 3 go routines (see below) are started right away.
// The 4th send operation blocks until a value is received from the channel by one of the
// first three go routines. foo is coded so that it finishes right after receive operation.
// So only 3 go routines get to run at the same time: one of them must receive from the channel
// before another could be run.
go foo(i)
}
}
sync.WaitGroup
object. WaitGroup
is another useful multithreading/synchronization type in Go standard library that has methods like Add
, Done
and Wait
. However, introducing the second synchronization object here is really unnecessary: we can finish the job using only that channel we started with.var ch = make(chan bool, 3) // The same buffered channel.
func foo(j int) {
fmt.Printf("running #%v\n", j)
<-ch // Same as before.
}
func main() {
for i := 0; i < 5; i++ {
ch <- true // Same as before.
go foo(i)
} // This loop sends 5 values to the channel. foo go routines receive 5 values.
// So if we waited “long enough” the channel would become empty.
// Let's wait "just enough" instead!
for j := 0; j < 3; j++ { // 3 is the buffer size AKA the channel capacity
ch <- true
}
// Each go routine receives just one previously sent value.
// After all that happens there are no more receivers.
// So the only way to send 3 more values in the second loop is to completely fill the channel buffer.
// For this to happen all go routines must receive from the channel (and finish).
// Thus by the time the 2nd loop is done all go routines must also be done!
}
That’s it for today. Here’s the link to a more idiomatic (and runnable "in browser") implementation of the last example. It has a bunch of extra diagnostic print statements. They, hopefully make it easier to understand what’s going on.f() ? g() : h()
is left associative in PHP (unlike any other C-like language).?:
its result can be an lvalue in C++: http://stackoverflow.com/questions/8535226/return-type-of-ternary-conditional-operator.