Search code examples
gochannelgoroutine

Using Mutex lock - still deadlock


I am playing around with Goroutines and channels and wonder why I got the error in the title.
The idea is that I have one global int channel that gets incremented per routing.
By using the mutex lock I expected the channel to be locked per routine but that failed.
The code is here:

package main

import (
    "fmt"
    "sync"
)

var number = make(chan int)
var mutex = &sync.Mutex{}

func worker(wg *sync.WaitGroup, id int) {
    defer wg.Done()

    mutex.Lock()
    number <- id + <-number
    mutex.Unlock()
}

func main() {
    var wg sync.WaitGroup
    number <- 0
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go worker(&wg, i)
    }

    wg.Wait()
    fmt.Println(<-number) // expected output: 0+1+2+3+4 = 10
}

https://play.golang.org/p/P5P9Bf5ZSIP


Solution

  • The issue here is to do with the channel you are using as it is unbuffered. An unbuffered channel will block until there is a receiver to receive the message.

    Here the main go routine adds a number to the channel then creates the 5 go routines to both take off the channel and add to the channel then waits for them to complete before taking an item off the channel. Adding 0 to the channel will not take place until there is something to receive the number off it so it blocks before it even reaches the mutex.

    The 5 go routines can only complete if there is something taking things off the channel.

    If you change to a buffered channel by supplying a size to the make call then this starts running to completion:

    package main
    
    import (
        "fmt"
        "sync"
    )
    
    var number = make(chan int, 5)
    var mutex = &sync.Mutex{}
    
    func worker(wg *sync.WaitGroup, id int) {
        defer wg.Done()
    
        mutex.Lock()
        number <- id + <-number
        mutex.Unlock()
    }
    
    func main() {
        var wg sync.WaitGroup
        number <- 0
        for i := 0; i < 5; i++ {
            wg.Add(1)
            go worker(&wg, i)
        }
    
        wg.Wait()
        fmt.Println(<-number) // expected output: 0+1+2+3+4 = 10
    }
    

    https://play.golang.org/p/QDXuDH0RGPC