浅谈Golang广播sync.Cond

简介: 浅谈Golang广播sync.Cond

前言


  1. sync.Cond的核心实现 - 通过一个锁,封装了notify 通知的实现,包括了单个通知与广播这两种方式
  2. sync.Cond与channel的异同 - channel应用于一收一发的场景,sync.Cond应用于多收一发的场景


代码

package main
import (
  "fmt"
  "sync"
  "time"
)
// 示例来自https://stackoverflow.com/questions/36857167/how-to-correctly-use-sync-cond
//fatal error: all goroutines are asleep - deadlock!
//goroutine 1 [sync.Cond.Wait]:
因为c.Wait()里面加锁,而外面没有解锁,造成死锁
func syncCondErr() {
  m := sync.Mutex{}
  c := sync.NewCond(&m)
  go func() {
    time.Sleep(1 * time.Second)
    c.Broadcast()
  }()
  m.Lock()
  time.Sleep(2 * time.Second)
  c.Wait()
}
//Tip:正确使用方式
func syncCondExplain() {
  m := sync.Mutex{}
  c := sync.NewCond(&m)
  // Tip: 主协程先获得锁
  c.L.Lock()
  go func() {
    // Tip: 协程一开始无法获得锁
    c.L.Lock()
    defer c.L.Unlock()
    fmt.Println("3. 该协程获得了锁")
    time.Sleep(2 * time.Second)
    // Tip: 通过notify进行广播通知,cond.Signal()用于随机一个单播
    c.Broadcast()
    fmt.Println("4. 该协程执行完毕,即将执行defer中的解锁操作")
  }()
  fmt.Println("1. 主协程获得锁")
  time.Sleep(1 * time.Second)
  fmt.Println("2. 主协程依旧抢占着锁获得锁")
  // Tip: 看一下Wait的大致实现,可以了解到,它是先释放锁,阻塞等待,直到收到了notify,又进行加锁
  c.Wait()
  // Tip: 记得释放锁
  c.L.Unlock()
  fmt.Println("Done")
}
//Tip:例子
func syncCond() {
  lock := sync.Mutex{}
  cond := sync.NewCond(&lock)
  for i := 0; i < 5; i++ {
    go func(i int) {
      cond.L.Lock()
      defer cond.L.Unlock()
      cond.Wait()
      fmt.Printf("No.%d Goroutine Receive\n", i)
    }(i)
  }
  time.Sleep(time.Second)
  cond.Broadcast()
  //cond.Signal()
  time.Sleep(time.Second)
}

源码分析

//Tip:wait先解锁,阻塞等待notify,得到后再加锁
func (c *Cond) Wait() {
  c.checker.check()
  t := runtime_notifyListAdd(&c.notify)
  c.L.Unlock()
  runtime_notifyListWait(&c.notify, t)
  c.L.Lock()
}
//广播
func (c *Cond) Broadcast() {
  c.checker.check()
  runtime_notifyListNotifyAll(&c.notify)
}
// notifyListNotifyAll notifies all entries in the list.
//go:linkname notifyListNotifyAll sync.runtime_notifyListNotifyAll
func notifyListNotifyAll(l *notifyList) {
  // Go through the local list and ready all waiters.
  for s != nil {
    next := s.next
    s.next = nil
    readyWithTime(s, 4)
    s = next
  }
}
//单播
func (c *Cond) Signal() {
  c.checker.check()
  runtime_notifyListNotifyOne(&c.notify)
}
// notifyListNotifyOne notifies one entry in the list.
//go:linkname notifyListNotifyOne sync.runtime_notifyListNotifyOne
func notifyListNotifyOne(l *notifyList) {
  for p, s := (*sudog)(nil), l.head; s != nil; p, s = s, s.next {
    if s.ticket == t {
      n := s.next
      if p != nil {
        p.next = n
      } else {
        l.head = n
      }
      if n == nil {
        l.tail = p
      }
      unlock(&l.lock)
      s.next = nil
      readyWithTime(s, 4)
      return
    }
  }
  unlock(&l.lock)
}


目录
相关文章
golang 系列:sync.Cond 机制
在 Go 里有专门为同步通信而生的 channel,所以较少看到 sync.Cond 的使用。不过它也是并发控制手段里的一种,今天我们就来认识下它的相关实现,加深对同步机制的运用。
537 0
golang 系列:sync.Cond 机制
|
3月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
124 4
Golang语言之管道channel快速入门篇
|
3月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
68 4
Golang语言文件操作快速入门篇
|
3月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
107 3
Golang语言之gRPC程序设计示例
|
3月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
93 4
|
3月前
|
Go
Golang语言错误处理机制
这篇文章是关于Golang语言错误处理机制的教程,介绍了使用defer结合recover捕获错误、基于errors.New自定义错误以及使用panic抛出自定义错误的方法。
51 3
|
3月前
|
Go 调度
Golang语言goroutine协程篇
这篇文章是关于Go语言goroutine协程的详细教程,涵盖了并发编程的常见术语、goroutine的创建和调度、使用sync.WaitGroup控制协程退出以及如何通过GOMAXPROCS设置程序并发时占用的CPU逻辑核心数。
55 4
Golang语言goroutine协程篇
|
3月前
|
Prometheus Cloud Native Go
Golang语言之Prometheus的日志模块使用案例
这篇文章是关于如何在Golang语言项目中使用Prometheus的日志模块的案例,包括源代码编写、编译和测试步骤。
65 3
Golang语言之Prometheus的日志模块使用案例
|
3月前
|
Go
Golang语言之函数(func)进阶篇
这篇文章是关于Golang语言中函数高级用法的教程,涵盖了初始化函数、匿名函数、闭包函数、高阶函数、defer关键字以及系统函数的使用和案例。
66 3
Golang语言之函数(func)进阶篇
|
2月前
|
前端开发 中间件 Go
实践Golang语言N层应用架构
【10月更文挑战第2天】本文介绍了如何在Go语言中使用Gin框架实现N层体系结构,借鉴了J2EE平台的多层分布式应用程序模型。文章首先概述了N层体系结构的基本概念,接着详细列出了Go语言中对应的构件名称,包括前端框架(如Vue.js、React)、Gin的处理函数和中间件、依赖注入和配置管理、会话管理和ORM库(如gorm或ent)。最后,提供了具体的代码示例,展示了如何实现HTTP请求处理、会话管理和数据库操作。
33 0