0

The following code implements the usage of two goroutines to alternately print elements in a linked list. However, it encounters a rather strange issue where the printed results are not visible without the final time.Sleep. In theory, stdout doesn't have a buffer. Can someone provide some guidance on this?

import (
    "context"
    "fmt"
    "sync"
)

type ListNode struct {
    val  int
    next *ListNode
}

func NewLinkedList() (head *ListNode) {
    var cur *ListNode
    for i := 0; i < 100; i++ {
        if cur == nil {
            cur = &ListNode{val: i}
            head = cur
        } else {
            cur.next = &ListNode{val: i}
            cur = cur.next
        }
    }
    return
}

func main() {
    ll := NewLinkedList()
    wg := sync.WaitGroup{}
    var a = make(chan *ListNode, 1)
    var b = make(chan *ListNode, 1)
    ctx, cancel := context.WithCancel(context.Background())
    worker := func(name string, input, output chan *ListNode) {
        wg.Add(1)
        defer wg.Done()
        for {
            select {
            case n := <-input:
                if n == nil {
                    break
                }
                fmt.Printf("%s: %d\n", name, n.val)
                if n.next != nil {
                    output <- n.next
                } else {
                    cancel()
                    break
                }
            case <-ctx.Done():
                break
            }
        }
    }

    go worker("a", a, b)
    go worker("b", b, a)

    a <- ll
    wg.Wait()
    //time.Sleep(time.Millisecond)
}

1 Answers1

1

You must call wg.Add(1) on the main goroutine, because it's a valid scenario that main() gets to wg.Wait() before the 2 launched goroutines could increment the waitgroup's counter. And if its counter is 0, wg.Wait() will not block, main() returns so the whole app terminates:

wg.Add(1)
go worker("a", a, b)
wg.Add(1)
go worker("a", a, b)

(And of course remove wg.Add(1) from the worker.)

See: Where to put wg.Add()

icza
  • 389,944
  • 63
  • 907
  • 827