跳到主要内容

goroutine使用

goroutinego 提供的轻量级并发编程工具,使用起来非常简单,在函数调用之前加上关键字go即可。例如:

package main

import (
"fmt"
"time"
)

func printMessage(from string) {
fmt.Println("print message from " + from)
}

func main() {

go printMessage("goroutine")
printMessage("main")

time.Sleep(1000 * time.Millisecond)
}

main方法里面调用了time.Sleep,这是因为 goroutine 需要调度, 如果 main 方法退出的时候 goroutine 还没有执行完成,就不会打印消息。

 在并发编程的场景中,经常需要协同。例如:main方法等待所有的 goroutine 执行完成。go 里实现协同的方式主要有以下两种:

  1. 使用管道(channel)
  2. 使用sync.WaitGroup

 管道(channel)是 go 中非常重要的数据类型,它允许向管道发送消息和从管道接受消息。<-操作符用来发送或者接受消息,箭头方向表示数据流向。

ch <- v    // 向管道发送消息

v := <-ch // 从管道获取消息,同时将值赋给v

 管道(channel)的特点是:如果对方没有准备好,发送或者接受将会阻塞。利用这个特性可以实现 main 方法等待 goroutine 执行完成。

package main

import (
"fmt"
"time"
)

func printMessage(from string, done chan bool) {
fmt.Println("print message from " + from)
time.Sleep(time.Second)
done <- true
}

func main() {

channel := make(chan bool)
go printMessage("goroutine", channel)
done := <-channel
fmt.Printf("\ngorountine finish %t\n", done)
fmt.Println("exit main")

}

 在main方法里等待channel里的消息,而消息会从 goroutine 发送。需要说明的是,上面代码里的channel变量是 unbuffered channel ,向 unbuffered channel 发送消息时会阻塞直至消息被接受,修改上面的代码即可验证发送消息是否阻塞。

package main

import (
"fmt"
"time"
)

func printMessage(from string, done chan bool) {
fmt.Println("print message from " + from)
done <- true
fmt.Println("send message success " + from)//main方法调用了休眠,故这条打印记录也等待了1s
}

func main() {

channel := make(chan bool)
go printMessage("goroutine", channel)
time.Sleep(time.Second)
done := <-channel
fmt.Printf("\ngorountine finish %t\n", done)
fmt.Println("exit main")

}

 与 unbuffered channel 对应的是 buffered channel,在调用make方法时,指定容量得到的结果便是 buffered channelbuffered channel 的特点是:当管道里未消费的消息超过容量时,发送消息才会会阻塞。

c := make(chan string, 2)

WaitGroup用来等待一组 goroutine 完成,使用WaitGroup分为这几步。

  1. 新建一个WaitGroup
  2. 调用WaitGroupAdd方法,参数为需要等待的 goroutine 数。
  3. 在每一个 goroutine 里调用WaitGroupDone方法(相当于减一)
  4. 在需要等待其他 goroutine 完成的 goroutine 里调用 WaitGroupWait方法,这样在其他 goroutine 完成之前,Wait方法会阻塞。
package main

import (
"fmt"
"sync"
)

func main() {

var wg sync.WaitGroup

wg.Add(3)

for i := 0; i < 3; i++ {
go func() {
defer wg.Done()
fmt.Println("go routine")
}()
}
wg.Wait()
fmt.Printf("\n goroutine finished")
}

  1. How to Wait for All Goroutines to Finish Executing Before Continuing

  2. Channels

  3. Go: Buffered and Unbuffered Channels

  4. Go by Example: Goroutines

署名-非商业性使用-禁止演绎 4.0 国际