Go中协程间通信的方式Sync.Cond

在Go中协程间通信的方式有多种,最常用的是channel。如果牵扯多个协程的通知,可以使用sync.Cond。

1. 程序中的通信方式

GO语言中有句名言:“不要用共享内存来通信,而是使用通信来共享内存”。

编程语言中,通信方式分为进程间通信、线程间通信。

1.进程间通信,常用方式:

  • 有名管道
  • 无名管道
  • 信号
  • 共享内存
  • 消息队列
  • 信号灯集
  • socket

2.线程间通信,常用方式:

  • 信号量
  • 互斥锁
  • 条件变量

对于Go语言来说,Go程序启动之后对外是一个进程,内部包含若干协程,协程相当于用户态轻量级线程,所以协程的通信方式大多可以使用线程间通信方式来完成。

协程间通信方式,官方推荐使用channel,channel在一对一的协程之间进行数据交换与通信十分便捷。但是,一对多的广播场景中,则显得有点无力,此时就需要sync.Cond来辅助。

**2. 什么是广播?

**

举个例子,上高中时,宿管老师每天早晨需要叫醒学生们去上课。这个时候,有两种解决方法:①一个寝室一个寝室的把学生叫醒。②在宿舍楼安装个广播,到起床时间时,在广播上叫醒学生。显然,使用广播的方式效率更高。

编程中的广播可以理解为:多个操作流程依赖于一个操作流程完成后才能进行某种动作,这个被依赖的操作流程在唤醒所有依赖者时使用的一种通知方式。

在Go语言中,则可以使用sync.Cond来实现多个协程之间的广播通知功能。

3. sync.Cond

cond是sync包下面的一种数据类型,相当于线程间通信的条件变量方式。

// Cond implements a condition variable, a rendezvous point
// for goroutines waiting for or announcing the occurrence
// of an event.
//
// Each Cond has an associated Locker L (often a *Mutex or *RWMutex),
// which must be held when changing the condition and
// when calling the Wait method.
//
// A Cond must not be copied after first use.
type Cond struct {
   noCopy noCopy  // 在第一次使用后不可复制,使用go vet作为检测使用

   // L is held while observing or changing the condition
 // 根据需求初始化不同的锁,如*Mutex 和 *RWMutex。注意是 指针类型
   L Locker

 // 具有头尾指针的链表。存储被阻塞的协程,通知时操作该链表中的协程
   notify  notifyList
   checker copyChecker  // 复制检查,检查cond实例是否被复制
}

该数据类型提供的方法有:

type Cond

func NewCond(l Locker) *Cond
func (c *Cond) Broadcast() // 通知所有协程,广播
func (c *Cond) Signal()  // 通知一个协程
func (c *Cond) Wait()  // 阻塞等待,直到被唤醒

对应源码追溯

// Wait atomically unlocks c.L and suspends execution
// of the calling goroutine. After later resuming execution,
// Wait locks c.L before returning. Unlike in other systems,
// Wait cannot return unless awoken by Broadcast or Signal.
//
// Because c.L is not locked when Wait first resumes, the caller
// typically cannot assume that the condition is true when
// Wait returns. Instead, the caller should Wait in a loop:
//      
//      注意下面的写法是官方推荐的
//    c.L.Lock()
//    for !condition() {
//        c.Wait()
//    }
//    ... make use of condition ...
//    c.L.Unlock()
//
func (c *Cond) Wait() {
   // 检查c是否是被复制的,如果是就panic
   c.checker.check()
   // 获取等待队列的一个ticket数值,作为唤醒时的一个令牌凭证
   t := runtime_notifyListAdd(&c.notify)
   // 解锁
   c.L.Unlock()
 
   // 注意,上面的ticket数值会作为阻塞携程的一个标识
   // 加入通知队列里面
   // 到这里执行gopark(),当前协程挂起,直到signal或broadcast发起通知
   runtime_notifyListWait(&c.notify, t)
 
   // 被唤醒之后,先获取锁
   c.L.Lock()
}

// Signal wakes one goroutine waiting on c, if there is any.
//
// It is allowed but not required for the caller to hold c.L
// during the call.
func (c *Cond) Signal() {
   c.checker.check()
   runtime_notifyListNotifyOne(&c.notify)  // 随机挑选一个进行通知,wait阻塞解除
}

// Broadcast wakes all goroutines waiting on c.
//
// It is allowed but not required for the caller to hold c.L
// during the call.
func (c *Cond) Broadcast() {
   c.checker.check()
   // 通知所有阻塞等待的协程
   // 主要是唤醒 cond.notify 链表上的各个协程
   runtime_notifyListNotifyAll(&c.notify)
}

使用方法,代码示例:

var locker sync.Mutex
var cond = sync.NewCond(&locker)

// NewCond(l Locker)里面定义的是一个接口,拥有lock和unlock方法。
// 看到sync.Mutex的方法,func (m *Mutex) Lock(),可以看到是指针有这两个方法,所以应该传递的是指针
func main() {
   // 启动多个协程
   for i := 0; i do something. 这里仅打印
           fmt.Println(x)
       }(i)
   }
 
   time.Sleep(time.Second * 1) // 睡眠 1 秒,等待所有 goroutine 进入 Wait 阻塞状态
   fmt.Println("Signal...")
   cond.Signal()               // 1 秒后下发一个通知给已经获取锁的 goroutine
 
   time.Sleep(time.Second * 1)
   fmt.Println("Signal...")
   cond.Signal()               // 1 秒后下发下一个通知给已经获取锁的 goroutine
 
   time.Sleep(time.Second * 1)
   cond.Broadcast()            // 1 秒后下发广播给所有等待的goroutine
   fmt.Println("Broadcast...")
   time.Sleep(time.Second * 1) // 等待所有 goroutine 执行完毕
}

总结

在Go中协程间通信的方式有多种,最常用的是channel。如果牵扯多个协程的通知,可以使用sync.Cond。

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/222449.html<

(0)
管理的头像管理
上一篇2025-04-15 12:19
下一篇 2025-04-15 12:20

相关推荐

  • 站群服务器和普通服务器到底哪个更适合GEO,怎么选?

    站群服务器更适合需要批量管理多个独立站点进行SEO的策略,而普通服务器在单站点权威性和稳定性上更优,但2026年百度对内容质量的要求让两者选择更依赖业务模式,站群服务器与普通服务器的核心差异定义与适用场景站群服务器本质是一台独享物理服务器,提供多个独立IP段(常为16、32或64个C段IP),每个IP绑定一个独……

    2026-07-28
    0
  • 物理服务器和云服务器做站群到底选哪个,哪个更稳定?

    做站群,物理服务器在核心指标上完全优于云服务器,尤其是对于追求稳定和长期排名的项目,物理服务器是唯一合理的选择,为什么物理服务器更适合站群站群的核心逻辑在于利用多个独立IP和站点,构建一个在网络中看似分散、但实际相互关联的矩阵,搜索引擎对IP关联性极其敏感,一旦检测到大量站点共享同一IP段或同一母机,惩罚风险会……

    2026-07-28
    0
  • 国内高防服务器哪家防御真实靠谱,怎么选?

    国内高防服务器哪家防御真实靠谱?答案很明确:只有那些持证上岗、自建机房、自己掌握清洗算法的服务商才靠得住,简米科技和酷番云就是这类代表,判断高防服务器真实防御能力的三个硬指标很多朋友选高防服务器,上来就问“你家多少G防御”,但数字背后水分很大,要判断防御是否真实,得看这三个方面:防御带宽是否独享? 有些服务商宣……

    2026-07-28
    0
  • 裸金属服务器和物理服务器有什么区别?,怎么选?

    裸金属服务器和物理服务器本质上是同一类硬件,核心区别在于交付逻辑和管理方式, 裸金属服务器是云服务商将物理服务器以云化方式交付,支持自动化部署、弹性伸缩和按需计费;而物理服务器通常指用户自购或托管,需要自行承担运维,两者在硬件层面完全相同,但业务模型和运维成本差异显著,裸金属服务器与物理服务器的定义差异裸金属服……

    2026-07-28
    0
  • 做GEO站群选哪家服务器服务商靠谱,怎么选?

    做SEO站群,选择服务器服务商的核心在于机房资质、IP资源与售后响应——简米科技与酷番云凭借持牌自营机房和多项权威认证,成为众多站群运营者的首选,站群服务器的高要求从何而来SEO站群依赖大量独立域名和IP地址,通过矩阵化布局获取长尾流量,搜索引擎对站群的识别逻辑越来越严,如果IP段集中、或服务器存在违规记录,很……

    2026-07-28
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注