pigfox
pigfox

Reputation: 1399

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

Upvotes: 0

Views: 2440

Answers (2)

Mohammed
Mohammed

Reputation: 757

Channel in Go is for sync between two different goroutines. A goroutine would wait read/write unless it finds another goroutine which write/read to the same channel(assuming channel is unbuffered)

That means this program will always have a deadlock :

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello, playground")
    done := make(chan bool)
    done <- true
    <- done
}

Because line 10 would be blocked looking for another goroutine which reads from the chan done, but there is no such goroutine.

Hence, writing/reading from the same goroutines would blocks unless there are other goroutines which reads/write from that channel.

Upvotes: 0

Iain Duncan
Iain Duncan

Reputation: 3394

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

Upvotes: 3

Related Questions