Not getting any output even if there is no race conditions

Issue

I am trying to create producer-consumer message queue system in Golang using buffered channel. Here is my implementation.

package main

import "fmt"

type MessageQueue struct {
    storage chan int
    count   int
}

var done = make(chan bool)

func NewMessageQueue(count int) *MessageQueue {
    ret := &MessageQueue{
        count:   count,
        storage: make(chan int, count),
    }
    return ret
}

func (m *MessageQueue) Produce() {
    for i := 0; i < m.count; i++ {
        m.storage <- i + 1
    }
    done <- true
}

func (m *MessageQueue) Consume(f func(int) int) {
    for each := range m.storage {
        fmt.Printf("%d ", f(each))
    }
}

func main() {
    op1 := func(a int) int {
        return a * a
    }
    msq := NewMessageQueue(10)
    go msq.Produce()
    go msq.Consume(op1)
    <-done
}

But unfortunately, I am not able to get the output when I run go run main.go however to check if there is any race condition or not, when I try go run -race main.go, I do get the output. I am unable to understand why it is happening. Can anyone help me here?

Solution

When your producer can send the values, it sends a value on the done channel so your app can terminate immediately.

Instead when the producer is done, it should close the m.storage channel, signalling no more values will be sent, and do not send a value on done, as you’re not done!

You’re done when values are consumed, so send a value on done in Consume():

func (m *MessageQueue) Produce() {
    for i := 0; i < m.count; i++ {
        m.storage <- i + 1
    }
    close(m.storage)
}

func (m *MessageQueue) Consume(f func(int) int) {
    for each := range m.storage {
        fmt.Printf("%d ", f(each))
    }
    done <- true
}

This will output (try it on the Go Playground):

1 4 9 16 25 36 49 64 81 100 

The done channel is required because consuming does not happen in the main goroutine, and the main goroutine must wait for it to end.

If you do the consuming on the main goroutine, you can remove the done channel:

msq := NewMessageQueue(10)
go msq.Produce()
msq.Consume(op1)

Try this one on the Go Playground.

Answered By – icza

Answer Checked By – Pedro (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.