Blocking operations

If a channel is full or its capacity is 0, then the operation will block. If we take the last example, which fills the channel and tries to execute another send operation, our application gets stuck:

func main() {
var (
a = make(chan int, 5)
)
for i := 0; i < 5; i++ {
a <- i
fmt.Println("a is", len(a), "/", cap(a))
}
a <- 0 // Blocking
}

The full example is available at https://play.golang.org/p/uSfm5zWN8-x.

When all goroutines are locked (in this specific case, we only have the main goroutine), the Go runtime raises a deadlock—a fatal error that terminates the execution of the application:

fatal error: all goroutines are asleep - deadlock!

This is can happen with both receive or send operations, and it's the symptom of an error in the application design. Let's take the following example:

func main() {
var a = make(chan int)
a <- 10
fmt.Println(<-a)
}

In the previous example, there is the a <- 10 send operation and the matching <-a receive operation, but nevertheless, it results in a deadlock. However, the channel we created has no capacity, so the first send operation will block. We can intervene here in two ways:

  • By increasing the capacity: This is a pretty easy solution that involves initializing the channel with make(chan int, 1). It works best only if the number of receivers is known a priori; if it is higher than the capacity, then the problem appears again.
  • By making the operations concurrent: This is a far better approach because it uses the channels for what they made for—concurrency.

Let's try to make the previous example work by using the second approach:

func main() {
var a = make(chan int)
go func() {
a <- 10
}()
fmt.Println(<-a)
}

Now, we can see that there are no deadlocks here and the program prints the values correctly. Using the capacity approach will also make it work, but it will be tailored to the fact that we are sending a single message, while the other method will allow us to send any number of messages through the channel and receive them accordingly from the other side:

func main() {
const max = 10
var a = make(chan int)

go func() {
for i := 0; i < max; i++ {
a <- i
}
}()
for i := 0; i < max; i++ {
fmt.Println(<-a)
}
}

The full example is available at https://play.golang.org/p/RKcojupCruB.

We now have a constant to store the number of operations executed, but there is a better and more idiomatic way to let a receiver know when there are no more messages. We will cover this in the next chapter about synchronization.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.54.255