4

Here is a code example taken from "Google I/O 2012 - Go Concurrency Patterns" (slides)

package main

import (
    "fmt"
    "time"
)

func main() {
    joe := boring("Joe")
    ann := boring("Ann")
    for i := 0; i < 5; i++ {
        fmt.Println(<-joe)
        fmt.Println(<-ann)
    }
    fmt.Println("The end")
}

func boring(msg string) <-chan string {
    c := make(chan string)
    go func() {
        for i := 0; ; i++ {
            c <- fmt.Sprintf("%s %d", msg, i)
            time.Sleep(time.Second)
        }
    }()
    return c
}

The output:

Joe 0
Ann 0
Joe 1
Ann 1
Joe 2
Ann 2
Joe 3
Ann 3
Joe 4
Ann 4
The end

This is the explanation by Rob Pike, the speaker (16:33 in the video): "...we're reading a value from Joe and a value from Ann. And because of the synchronization nature of the channels, the two guys are taking turns, not only in printing the values out, but also in executing them. Because if Ann is ready to send a value but Joe hasn't done that yet, Ann will still be blocked, waiting to deliver the value to main."

This confused me. What's the meaning of "if Ann is ready to send a value but Joe hasn't done that yet, Ann will still be blocked"? As we know, a channel builds the communication between two goroutines and synchronizes their executions. But we have crated two channels here (joe and ann). The main goroutine talks with two new goroutines via joe and ann, respectively. Does that mean the synchronization nature is also valid between channels? Or that the main goroutine can talk with only one other goroutine at a time?

Michael Laszlo
  • 12,009
  • 2
  • 29
  • 47
Pauli
  • 1,159
  • 1
  • 11
  • 22

3 Answers3

7

It just means those channels are unbuffered.

    fmt.Println(<-joe)
    fmt.Println(<-ann)

The second line won't be able to execute before the first completes.
And the first won't complete until something writes in joe's channel.

Ann won't be able to write in Ann's channel if that channel has already a value.
And that channel won't be read (fmt.Println(<-ann)) until joe's channel is read first (fmt.Println(<-joe)).

Both channels are independent (unaware of each other), but the sequential nature of the read operations make one waiting for the other to be read first.

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
2

The channels are unbuffered.

c := make(chan string)

The capacity, in number of elements, sets the size of the buffer in the channel. If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and receiver are ready. Otherwise, the channel is buffered and communication succeeds without blocking if the buffer is not full (sends) or not empty (receives). A nil channel is never ready for communication.

The receives will be in sequence, joe followed by ann.

fmt.Println(<-joe)
fmt.Println(<-ann)
peterSO
  • 158,998
  • 31
  • 281
  • 276
2

You are characterizing the situation incorrectly when you say that "a channel builds the communication between two goroutines and synchronizes their executions".

The synchronization that's happening is within the channel. When you try to read data from the channel, the read action is blocked until data has been written to the channel.

There are two different channels in this example, and neither one of them knows anything about the other. The significant detail is that this line blocks until data has been written to the joe channel:

fmt.Println(<-joe)

That is what prevents the following line from being executed until "Joe" has been printed.

Michael Laszlo
  • 12,009
  • 2
  • 29
  • 47