控制并发有两种经典的方式,一种是WaitGroup,另一种就是Context安全
func main() { var wg sync.WaitGroup wg.Add(2) go func() { time.Sleep(2*time.Second) fmt.Println("1号完成") wg.Done() }() go func() { time.Sleep(2*time.Second) fmt.Println("2号完成") wg.Done() }() wg.Wait() fmt.Println("好了,你们都干完了,放工") }
以上例子必定要等到两个goroutine同时作完才会所有完成,这种控制并发方式尤为适用于多个goroutine协同作一件事情的时候。
func main() { stop := make(chan bool) go func() { for { select { case <-stop: fmt.Println("监控退出,中止了...") return default: fmt.Println("goroutine监控中...") time.Sleep(2 * time.Second) } } }() time.Sleep(10 * time.Second) fmt.Println("能够了,通知监控中止") stop<- true //为了检测监控过是否中止,若是没有监控输出,就表示中止了 time.Sleep(5 * time.Second) }
例子中咱们经过select判断stop是否接受到值,若是接受到值就表示能够推出中止了,若是没有接受到,就会执行default里面的监控逻辑,继续监控,直到收到stop的通知 以上控制goroutine的方式在大多数状况下能够知足咱们的使用,可是也存在不少局限性,好比有不少goroutiine,而且这些goroutine还衍生了其余goroutine,此时chan就比较困难解决这样的问题了
以上问题是存在的,好比一个网络请求request,每一个request都须要开启一个goroutine作一些事情。因此咱们须要一种能够跟踪goroutine的方案才能够达到控制的目的,go为咱们提供了Context网络
func main() { ctx, cancel := context.WithCancel(context.Background()) go watch(ctx,"【监控1】") go watch(ctx,"【监控2】") go watch(ctx,"【监控3】") time.Sleep(10 * time.Second) fmt.Println("能够了,通知监控中止") cancel() //为了检测监控过是否中止,若是没有监控输出,就表示中止了 time.Sleep(5 * time.Second) } func watch(ctx context.Context, name string) { for { select { case <-ctx.Done(): fmt.Println(name,"监控退出,中止了...") return default: fmt.Println(name,"goroutine监控中...") time.Sleep(2 * time.Second) } } }
例子中启动了3个监控goroutine进行不断的监控,每个都使用Context进行跟踪,当咱们使用cancel函数通知取消时候,这3个 goroutine都会被结束。全部基于这个context或者衍生出来的子Context都会收到通知,这样就能够进行清理操做最终释放goroutine了并发
Context是一个接口,具体的内容以下:
~go~函数
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) func WithValue(parent Context, key, val interface{}) Context
这四个With函数,接收的都有一个partent参数,就是父Context,咱们要基于这个父Context建立出子Context的意思线程