How to use a method as a goroutine function

Issue

I have this code. I expect to output:

hello : 1 
world : 2

but it outputs:

world : 2
world : 2

Is there something wrong with my code?

package main

import (
    "fmt"
    "time"
)

type Task struct {
    name string
    data int32
}

func (this *Task) PrintData() {
    fmt.Println(this.name, ":", this.data)
}

func main() {
    tasks := []Task{{"hello", 1}, {"world", 2}}
    for _, task := range tasks {
        go task.PrintData()
    }
    time.Sleep(time.Second * 5000)
}

Solution

Because PrintData is a pointer receiver and task is a value, the compiler automatically takes the address of task when making the method call. The resulting call is the same as (&task).PrintData().

The variable task is set to a different value on each iteration through the loop. The first goroutine doesn’t run until task is set to second value. Run this example to see that the same address is passed to PrintData on each iteration.

There are a few ways to fix this. The first is to use *Task in the slice:

tasks := []*Task{{"hello", 1}, {"world", 2}}
for _, task := range tasks {
    go task.PrintData()
}

playground example

The second is to create a new variable inside the loop:

tasks := []Task{{"hello", 1}, {"world", 2}}
for _, task := range tasks {
    task := task
    go task.PrintData()
}

playground example

A third is to take the address of the slice element (using the automatically inserted address operation):

tasks := []Task{{"hello", 1}, {"world", 2}}
for i := range tasks {
    go tasks[i].PrintData()
}

playground example

Yet another option is to change PrintData to a value receiver to prevent the method call from automatically taking the address of task:

func (this Task) PrintData() {
    fmt.Println(this.name, ":", this.data)
}

playground example

This issue is similar to the issue discussed in the closures and goroutines FAQ. The difference between the issues is the mechanism used to pass a pointer to the goroutine function. The code in the question uses the method’s receiver argument. The code in the FAQ uses a closure.

Answered By – Bayta Darell

Answer Checked By – Jay B. (GoLangFix Admin)

Leave a Reply

Your email address will not be published.