WaitGroup with Channels

Issue

I’ve been playing around with this and came up with:

type Function struct{
    Function func(*TaskGroup, []interface{})
    Args []interface{}
}

type TaskGroup struct{
    Group sync.WaitGroup
    Functions []Function
}

func (x *TaskGroup) Start() {
    for _, Function := range x.Functions{
        x.Group.Add(1)
        go Function.Function(x, Function.Args)
    }
    x.Group.Wait()
}

In order to work easier with mutliple functions I have to wait for.

The following tests will work and I do not understand why:

func auxC(x *TaskGroup, args []interface{}){
    defer x.Group.Done()
    messageOut := args[0].(chan string)
    messageOut <- "TestC"
}
func auxD(x *TaskGroup, args []interface{}){
    defer x.Group.Done()
    messageOut := args[0].(chan string)
    messageOut <- "TestD"
}

func TestTaskGroupBaseB(t *testing.T) {
    messageC := make(chan string, 1)
    messageD := make(chan string, 1)

    tg := TaskGroup{
        Functions: []Function{
            {auxC, []interface{}{messageC}},
            {auxD, []interface{}{messageD}},
        },
    }
    tg.Start()

    fmt.Println(<- messageC)
    fmt.Println(<- messageD)

    time.Sleep(100 * time.Millisecond)
}

I first tried with unbuffered channels like this:

messageC := make(chan string)
messageD := make(chan string)

But it doesn’t work it just gets stuck forever without doing anything, so i have a few questions:

  1. Why do buffered channels with size 1 work while unbuffered doesn’t?
  2. Isn’t unbuffered by default size 1?

Refactored code, see comments:

Main/Tests:

func auxC(args []interface{}){
    messageOut := args[0].(chan string)
    messageOut <- "TestC"
}
func auxD(args []interface{}){
    messageOut := args[0].(chan string)
    messageOut <- "TestD"
}

func TestTaskGroupBaseB(t *testing.T) {
    messageC := make(chan string,1)
    messageD := make(chan string,1)

    tg := TaskGroup{
        Functions: []Function{
            {auxC, []interface{}{messageC}},
            {auxD, []interface{}{messageD}},
        },
    }
    tg.Wait()

    fmt.Println(<- messageC)
    fmt.Println(<- messageD)

    time.Sleep(100 * time.Millisecond)
}

Task Group:

type Function struct{
    Function func([]interface{})
    Args []interface{}
}

type TaskGroup struct{
    Group sync.WaitGroup
    Functions []Function
}

func (x *TaskGroup) Wait() {
    for _, function := range x.Functions{
        x.Group.Add(1)
        go func(x *TaskGroup, f Function){
            defer x.Group.Done()
            f.Function(f.Args)
        }(x, function)
    }
    x.Group.Wait()
}

Solution

Using a channel with buffer size 1, first write buffers data, then goroutines end and you can read the buffered data in the main goroutine.

When channel size is zero a write to the channel blocks until another goroutine reads from it. So both of your goroutines are waiting to write the channels. If you move the Wait() call after the channel reads in main it should work.

Answered By – Burak Serdar

Answer Checked By – Gilberto Lyons (GoLangFix Admin)

Leave a Reply

Your email address will not be published.