使用 sync.Cond 来协调并发 goroutine 的访问共享资源

开发 前端
互斥锁(sync.Mutex)用于保护临界区和共享资源,而 sync.Cond​ 则用于协调多个 goroutine​ 的执行顺序。互斥锁只能一个 goroutine​ 持有锁,其他 goroutine​ 必须等待锁被释放才能继续执行。而 sync.Cond​ 可以让等待的 goroutine 在条件满足时被唤醒,进而继续执行。

使用 sync.Cond 解决并发访问共享资源问题

在并发编程中,当多个 goroutine 需要访问共享资源时,我们需要使用一些机制来协调它们的执行顺序,以避免竞态条件和数据不一致的问题。在 Go 语言中,sync.Cond 条件变量就是一种常用的机制,它可以用来等待和通知其他 goroutine。

sync.Cond 和互斥锁的区别

互斥锁(sync.Mutex)用于保护临界区和共享资源,而 sync.Cond 则用于协调多个 goroutine 的执行顺序。互斥锁只能一个 goroutine 持有锁,其他 goroutine 必须等待锁被释放才能继续执行。而 sync.Cond 可以让等待的 goroutine 在条件满足时被唤醒,进而继续执行。

sync.Cond 的四个方法

sync.Cond 的定义如下:

// 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

        // L is held while observing or changing the condition
        L Locker

        notify  notifyList
        checker copyChecker
}

每个 Cond 实例都会关联一个锁 L(互斥锁 *Mutex,或读写锁 *RWMutex),当修改条件或者调用 Wait 方法时,必须加锁。

1. NewCond 创建实例

func NewCond(l Locker) *Cond

NewCond 方法用于创建一个 Cond 实例,并关联一个锁(互斥锁或读写锁)。

2. Broadcast 广播唤醒所有等待的 goroutine

// 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()

Broadcast 方法用于唤醒所有等待条件变量 c 的 goroutine。它不需要持有锁来调用。

3. Signal 唤醒一个等待的 goroutine

// 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()

Signal 方法用于唤醒一个等待条件变量 c 的 goroutine。它不需要持有锁来调用。

4. 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()

Wait 方法会自动释放锁,并挂起当前的 goroutine,直到条件变量 c 被 Broadcast 或 Signal 唤醒。被唤醒后,Wait 方法会重新获得锁,并继续执行后续的代码。

使用示例

下面是一个使用 sync.Cond 的示例,实现了一个简单的读写同步机制:

package main

import (
    "fmt"
    "sync"
    "time"
)

var done = false

func read(str string, c *sync.Cond) {
    c.L.Lock()
    for !done {
        c.Wait()
    }
    fmt.Println(str, "start reading")
    c.L.Unlock()
}

func write(str string, c *sync.Cond) {
    fmt.Println(str, "start writing")
    time.Sleep(2 * time.Second)
    c.L.Lock()
    done = true
    c.L.Unlock()
    fmt.Println(str, "wake up all")
    c.Broadcast()
}

func main() {
    m := &sync.Mutex{}
    c := sync.NewCond(m)

    go read("reader1", c)
    go read("reader2", c)
    write("writer", c)

    time.Sleep(5 * time.Second)
}

在这个示例中,有两个读取协程(reader1 和 reader2)和一个写入协程(writer)。写入协程在执行后会通知所有等待的读取协程,读取协程在条件满足时才能开始读取。

输出结果如下:

writer start writing
writer wake up all
reader2 start reading
reader1 start reading

通过使用 sync.Cond,我们可以很方便地实现多个 goroutine 之间的等待和通知机制,从而更好地协调并发访问共享资源的执行顺序。

责任编辑:武晓燕 来源: 爱发白日梦的后端
相关推荐

2023-06-26 08:28:35

Sync.CondGolang

2021-07-06 07:46:07

Go语言编程

2021-05-21 08:21:57

Go语言基础技术

2009-01-08 09:54:00

2023-12-24 12:33:20

互斥锁Go代码

2021-09-30 09:21:28

Go语言并发编程

2011-03-02 09:59:01

Ubuntuvsftpd

2011-09-01 09:18:36

2020-02-21 20:21:45

线程共享资源

2020-09-16 07:56:28

多线程读写锁悲观锁

2021-04-02 09:50:14

微服务分布式锁Java

2020-03-06 08:00:06

Zookeeper分布式系统

2023-06-16 08:36:25

多线程编程数据竞争

2023-05-18 08:38:13

Java锁机制

2023-06-02 08:29:24

https://wwMutex

2023-12-07 12:32:57

Java死锁线程

2023-11-10 08:44:13

分布式锁分布式系统

2023-07-27 08:59:19

线程同步Python

2013-11-13 10:16:35

局域网共享文件

2011-07-20 09:25:19

域控制器用户
点赞
收藏

51CTO技术栈公众号