脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|shell|

服务器之家 - 脚本之家 - Golang - Go中sync 包Cond使用场景分析

Go中sync 包Cond使用场景分析

2023-03-05 12:05水淹萌龙 Golang

Cond 是和某个条件相关,在条件还没有满足的时候,所有等待这个条件的协程都会被阻塞住,只有这个条件满足的时候,等待的协程才可能继续进行下去,这篇文章主要介绍了Go中sync 包的Cond使用场景分析,需要的朋友可以参考下

背景

编写代码过程中, 通常有主协程和多个子协程进行协作的过程,比如通过 WaitGroup 可以实现当所有子协程完成之后, 主协程再继续执行, 具体可参考:Go 中goroutine和WaitGroup的使用

如上的场景是主协程等待子协程达到某个状态再继续运行。 但是反过来怎么操作呢,要求一组子协程等待主协达到某个状态时才继续运行。这个时候就需要用到 Cond 了

Cond 简介

Cond 是和某个条件相关,在条件还没有满足的时候,所有等待这个条件的协程都会被阻塞住,只有这个条件满足的时候,等待的协程才可能继续进行下去。
Cond 在初始化的时候,需要关联一个 Locker 接口的实例,一般会使用 Mutex 或者 RWMutex。
Cond 关联的 Locker 实例可以通过 c.L 访问,它内部维护着一个先入先出的等待队列。
Cond 分别有三个方法

  • Wait

会把当前协程放入Cond的等待队列中并阻塞,直到被Signal或者Broadcast方法从等待队列中移除并唤醒,用于子协程阻塞。

  • Signal

主协程唤醒等待队列中的一个子协程,先唤醒最先阻塞的子协程,被唤醒的子协程继续执行。

  • Broadcast

主协程唤醒等待队列中的全部协程,所有子协程继续执行。

注意:调用Signal和Broadcast方法,不强求持有c.L的锁,调用Wait方法是必须要持有c.L的锁。

使用示例

Signal的使用场景

大家都去医院先排队,然后等待叫号,先排队的先叫号。这次模拟有5个病人,分别先排队。 然后护士根据排队先后来叫号;
具体场景是,5个病人在三秒中之内分别排号,护士今天要叫5个号,一秒叫一个,叫完5个号就结束了
代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main
import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)
 
func main() {
    c := sync.NewCond(&sync.Mutex{})
    num := 0
    // 当前叫号是几号
    hand_num := 0
    for i := 0; i < 5; i++ {
        go func(i int) {
            // 分别在不同时间排队
            time.Sleep(time.Second * time.Duration(rand.Int63n(10)))
            c.L.Lock()
            num++
            // 当前取得号。
            cur := num
            fmt.Printf("%s  %d 号病人取到了 %d 号\n", time.Now().Format("2006-01-02 15:04:05"), i, cur)
            // 取到号了,等待叫号
            c.Wait()
            fmt.Printf("%s  %d 号病人排队号是 %d 号,被叫号了\n", time.Now().Format("2006-01-02 15:04:05"), i, cur)
            hand_num = cur
            c.L.Unlock()
        }(i)
    }
 
    // 都叫号了
    for hand_num != 5 {
        // 叫号
        c.Signal()
        time.Sleep(time.Second * 1)
    }
 
    time.Sleep(time.Second * 10)
}

执行结果如下

Go中sync 包Cond使用场景分析

结果表明,5个病人,分别在三秒钟内先后取号, 然后护士每过一秒钟按照排队的先后顺序叫一个号(叫号的过程依然有病人取号),先取号的被先叫号。
此场景中,5个病人相当于5个协程, 主协程反复使用Signal() 按照顺序一个个唤醒阻塞的子协程。

Broadcast的使用场景

场景为如下: 运动员跑步比赛,要求8秒内全部运动员准备好,然后等待教练发令, 教练10秒后发令,所有运动员在发令后开始跑。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package main
 
import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)
 
func main() {
    c := sync.NewCond(&sync.Mutex{})
 
    for i := 0; i < 10; i++ {
        go func(i int) {
            // 随机一个8秒内的准备时间
            time.Sleep(time.Second * time.Duration(rand.Int63n(8)))
            fmt.Printf("%s 运动员%d已准备就绪\n", time.Now().Format("2006-01-02 15:04:05"), i)
            c.L.Lock()
            // 准备完毕,等待教练发令
            c.Wait()
            c.L.Unlock()
            fmt.Printf("%s 运动员%d开跑\n", time.Now().Format("2006-01-02 15:04:05"), i)
        }(i)
    }
 
    // 主协程等待10秒后发令
    time.Sleep(time.Second * 10)
    fmt.Printf("%s 教练发令。\n", time.Now().Format("2006-01-02 15:04:05"))
    // 教练发令。通知所有运动员开始跑步, 即唤起之前 wait()的所有协程
    c.Broadcast()
    // 等待跑步
    time.Sleep(time.Second * 5)
}

执行结果如下:

Go中sync 包Cond使用场景分析

如结果所示, 10个运动员在8秒内分别准备好,等待教练发令后,同时开跑。
此场景中,10个运动员相当于10个协程, 同时等待主协程的命令,使用Broadcast() 唤醒所有阻塞的子协程。

注意事项

使用 Cond,最容易踩的坑就是调用 Wait() 方法之前,调用者没有持有锁或没有检查辅助条件。
在如上示例代码中,假如把调用 Wait() 方法前后的加锁和释放锁的代码注释掉,运行代码会导致程序 panic。原因是调用 Wait 方法,会先把调用者放入等待队列中,然后释放锁。此时如果在未持有锁时调用释放锁的方法,就会导致程序 panic。

到此这篇关于Go中sync 包的 Cond 使用的文章就介绍到这了,更多相关go sync包cond使用内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/qq_21047625/article/details/129280580

延伸 · 阅读

精彩推荐
  • Golanggo GCM gin中间件的加密解密文件流处理

    go GCM gin中间件的加密解密文件流处理

    这篇文章主要介绍了go语言 GCM加密解密,gin中间件的加密解密及文件流处理详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早...

    dz4569310062022-10-09
  • Golanggo+redis实现消息队列发布与订阅的详细过程

    go+redis实现消息队列发布与订阅的详细过程

    这篇文章主要介绍了go+redis实现消息队列发布与订阅,redis做消息队列的缺点:没有持久化,一旦消息没有人消费,积累到一定程度后就会丢失,本文给大家...

    升级打怪8652022-11-20
  • Golanggo语言中切片Slice与数组Array对比以及panic: runtime error: index out of range问题解决

    go语言中切片Slice与数组Array对比以及panic: runtime error: index out o

    go语言中数组与其他语言有在显著的不同,包括其不能够进行添加,以及值拷贝的特性,下面这篇文章主要给大家介绍了关于go语言中切片Slice与数组Array对比以...

    大锤爱编程10632022-07-20
  • GolangGo gRPC教程实现Simple RPC

    Go gRPC教程实现Simple RPC

    这篇文章主要为大家介绍了Go gRPC教程实现Simple RPC示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪...

    烟花易冷人憔悴7372022-10-20
  • GolangGolang中Set类型的实现方法示例详解

    Golang中Set类型的实现方法示例详解

    这篇文章主要给大家介绍了关于Golang中Set类型实现的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要...

    脚本之家7292020-05-09
  • Golang使用go的interface案例实现多态范式操作

    使用go的interface案例实现多态范式操作

    这篇文章主要介绍了使用go的interface案例实现多态范式操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    涛歌依旧5162021-03-19
  • GolangGolang库插件注册加载机制的问题

    Golang库插件注册加载机制的问题

    这篇文章主要介绍了Golang库插件注册加载机制,这里说的插件并不是指的golang原生的可以在buildmode中加载指定so文件的那种加载机制,需要的朋友可以参考下...

    轩脉刃7332022-09-12
  • GolangGo语言利用ffmpeg转hls实现简单视频直播

    Go语言利用ffmpeg转hls实现简单视频直播

    这篇文章主要为大家介绍了Go语言利用ffmpeg转hls实现简单视频直播,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪...

    xiaoyaoyou.xyz9732022-09-29