Search code examples
gogoroutine

Writing to a slice by a channel and a goroutine: why slice is eventually empty


I run this function:

func Run() () {
    // This slice is going to be filled out by a channel and goroutine.
    vertices := make([]Vertex, 0)

    var wg sync.WaitGroup

    // Obtain a writer to fill out the vertices.
    writer := Writer(&wg, vertices)

    // Run an arbitrary logic to send data to writer.
    Logic(writer)

    // Stop the writer reading on the channel.
    close(writer)

    // Wait for the write to complete.
    wg.Wait()

    // See if vertices slice is actually filled out.
    DoublecheckVertices(vertices)
}

But eventually, my vertices slice is empty:

func DoublecheckVertices(vertices []Vertex) () {
    // Here I notice that `vertices` slice is actually empty :(

}

The function which returns the writer is something like this:

func Writer(wg *sync.WaitGroup, vertices []Vertex) (chan<- []*Triangle3) {
    // External code writes to this channel.
    // This goroutine reads the channel and writes to vertices.
    writer := make(chan []*Triangle3)

    // Write by a goroutine.
    wg.Add(1)
    go func() {
        defer wg.Done()

        a := Vertex{}

        // Read from the channel and write them to vertices.
        for ts := range writer {
            for _, t := range ts {
                a.X = float32(t.V[0].X)
                a.Y = float32(t.V[0].Y)
                a.Z = float32(t.V[0].Z)
                vertices = append(vertices, a)
            }
        }
    }()

    return writer
}

Can anybody help me with figuring out why my vertices slice is eventually empty?

Logs

Logs indicate that the vertices slice is actually filled out. But for some reason, it's empty when it's passed to DoublecheckVertices.

                vertices = append(vertices, a)
                // This Log shows the slice is actually filled out:
                fmt.Printf("vertices len() is %v\n", len(vertices))

Solution

  • That seems similar to "Pass slice as function argument, and modify the original slice"

    If you want your goroutine to modify the slice you created outside, you would need a pointer to that slice:

    func Writer(wg *sync.WaitGroup, vertices *[]Vertex) (chan<- []*Triangle3) {
        // External code writes to this channel.
        // This goroutine reads the channel and writes to vertices.
        writer := make(chan []*Triangle3)
    
        // Write by a goroutine.
        wg.Add(1)
        go func() {
            defer wg.Done()
    
            a := Vertex{}
    
            // Read from the channel and write them to vertices.
            for ts := range writer {
                for _, t := range ts {
                    a.X = float32(t.V[0].X)
                    a.Y = float32(t.V[0].Y)
                    a.Z = float32(t.V[0].Z)
                    *vertices = append(*vertices, a)  <=====
                }
            }
        }()
    
        return writer
    }