golang编程细讲-并发-协程同步

书接上回,在golang中使用go关键字即可创建协程。实际上,一个golang脚本运行起来后,应用主协程外,还有协程调度器, gc等多个“系统”级的协程存在,不过我们一般不用太过度关注它们。

我们使用go关键字创建的协程,与我们的应用主协程之间,是会被并行调度的,因此执行的顺序是不确定的。它们之间需要必定的同步机制。

package main

import "time"

func main() {
    go func() {
        println("这是子协程")
    }()
    time.Sleep(2 * time.Second)
    println("这是主协程")
    println("done.")
}

运行:

> go run main.go
这是子协程
这是主协程
done.

我们看到了主协程使用了time.Sleep等待子协程的执行结束。这是一种最简单的方式,只适合用在有限的场景。

golang中提供了sync.WaitGroup用于协程的同步:

package main

import (
    "sync"
)

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(idx int) {
            defer wg.Done()

            println("这是子协程:", idx)
        }(i)
    }
    wg.Wait()
    println("这是主协程")
    println("done.")
}

运行:

> go run main.go
这是子协程: 0
这是子协程: 4
这是子协程: 1
这是子协程: 2
这是子协程: 3
这是主协程
done.

我们看到了通过使用sync.WaitGroup,保证了所有相关协程在某个点上的同步结束。注意Add/Done/Wait等成员函数被调用的位置。

channel类型一般用于协程间的,包括主协程与子协程间数据的通信。同时通过channel的关闭也可以通知子协程结束:

package main

import (
	"sync"
)

func main() {
    ch := make(chan bool, 10)

    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(idx int, _ch chan bool) {
            defer wg.Done()

            for {
                select {
                case _, ok := <-_ch:
                    if !ok { // channel is closed
                        println("子协程关闭:", idx)
                        return
                    }
              }
          }
        }(i, ch)
    }
    close(ch)
    wg.Wait()
    println("这是主协程")
    println("done.")
}

运行:

> go run main.go
子协程关闭: 0
子协程关闭: 1
子协程关闭: 3
子协程关闭: 4
子协程关闭: 2
这是主协程
done.

当然,也可以使用context(上下文)通知子协程结束:

package main

import (
    "context"
    "sync"
    "time"
)

func main() {
    ctx, cancel := context.WithCancel(context.Background())

    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(idx int, _ctx context.Context) {
            defer wg.Done()

            for {
                select {
                  case <-ctx.Done():
                      println("子协程关闭:", idx)
                      return
                  default:
                      // 子协程处理逻辑
                      time.Sleep(1 * time.Second)
                  }
            }
        }(i, ctx)
    }
    cancel()
    wg.Wait()
    println("这是主协程")
    println("done.")
}

运行:

> go run main.go
子协程关闭: 4
子协程关闭: 3
子协程关闭: 0
子协程关闭: 2
子协程关闭: 1
这是主协程
done.

最后,sync.Mutex(锁)和sync.RWMutex(读写锁), sync.Cond也算是另外的同步机制,控制着对共享资源的并发访问。

总结一下,在golang中,我们一般使用sync.WaitGroup进行并发协程的同步处理,同时会使用channel的关闭和Context(上下文)进行协程间的通知。

我们明天聊协程间数据的通信机制:channel

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容