Golang concurrent wait

Posted by jateeq on Mon, 17 Jan 2022 04:24:31 +0100

Q & A in the previous section

In the previous section, a reader asked how big the goroutine stack size is. I made a detailed query

About goroutine stack size Official documents As described in, the minimum is 4kb before 1.2, becomes 8kb at 1.2, and can be used SetMaxStack Set the maximum stack size.

stay runtime/debug The package can control the stack size of the largest single goroutine. The default is 1GB on 64 bit systems and 250MB on 32-bit systems.

Because each goroutine needs to be able to run, they have their own stack. If each goroutine allocates a fixed stack size and cannot grow, too small will lead to overflow, too large will waste space, and many goroutines cannot exist.

So in Version 1.3 In, change to continuous stack( Continuous stack ), in order to solve this problem, goroutine can initially allocate only a small space (8KB) to the stack, and then automatically grow as needed in the process of use. This is why Go can open thousands of goroutines without running out of memory.

Version 1.4 goroutine stack reduced from 8Kb to 2Kb

Golang concurrent wait

Source location of this section https://github.com/golang-minibear2333/golang/blob/master/4.concurrent/goroutine-wait/

brief introduction

Goroutine is a very useful function in Golang. Sometimes goroutine returns before it finishes executing the function. What should I do if I want to wait for the current goroutine to finish executing and then proceed?

func say(s string) {
    for i := 0; i < 3; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

func main() {
    go say("hello world")
    fmt.Println("over!")
}

Output over, The main thread is not waiting

Use Sleep to wait

func main() {
    go say("hello world")
    time.Sleep(time.Second*1)
    fmt.Println("over!")
}

Run the modified program and the results are as follows:

hello world
hello world
hello world
over!

The result is as expected, but it is too low. We don't know how long we should wait in the actual implementation, so we can't accept this scheme!

Send signal

func main() {
    done := make(chan bool)
    go func() {
        for i := 0; i < 3; i++ {
            time.Sleep(100 * time.Millisecond)
            fmt.Println("hello world")
        }
        done <- true
    }()

    <-done
    fmt.Println("over!")
}

The output results are the same as above and meet expectations

This method cannot handle multiple processes, so it is not an elegant solution.

WaitGroup

Golang officially provides WaitGroup type in sync package to solve this problem. The document description is as follows:

The usage methods can be summarized as follows:

  • Create a WaitGroup instance in the parent collaboration, for example, with the name: wg
  • Call WG Add (n), where n is the number of goroutine s waiting
  • Execute defer WG in each function that goroutine runs Done()
  • Call WG Wait() blocks the main logic
  • Until all goroutine execution is completed.
func main() {
    var wg sync.WaitGroup
    wg.Add(2)
    go say2("hello", &wg)
    go say2("world", &wg)
    fmt.Println("over!")
    wg.Wait()
}

func say2(s string, waitGroup *sync.WaitGroup) {
    defer waitGroup.Done()

    for i := 0; i < 3; i++ {
        fmt.Println(s)
    }
}

Output, note that the order is chaotic because of concurrent execution

hello
hello
hello
over!
world
world
world

Beware of defects

For a short example, note that the variables passed in by the loop are replaced by intermediate variables to prevent closure bug s

func errFunc() {
	var wg sync.WaitGroup
	sList := []string{"a", "b"}
	wg.Add(len(sList))
	for _, d := range sList {
		go func() {
			defer wg.Done()
			fmt.Println(d)
		}()
	}
	wg.Wait()
}

Output, you can find that all become the last one

b
b

The parent and child processes are concurrent. The for loop on the parent coroutine is executed instantaneously, and the internal coroutine uses the last value of d, which is the closure problem.

The solution is passed in as a parameter

func correctFunc() {
	var wg sync.WaitGroup
	sList := []string{"a", "b"}
	wg.Add(len(sList))
	for _, d := range sList {
		go func(str string) {
			defer wg.Done()
			fmt.Println(str)
		}(d)
	}
	wg.Wait()
}

output

b
a

Note that the value in range may have a pit that may be encountered in 1.7.3! (in e-book golang.coding3min.com)

quote

Getting started with Golang: wait for goroutine to complete the task

Topics: Go