c语言编程笔录

首页 >   > 笔记大全

笔记大全

Go中sync包Cond使用场景是什么

更新时间:2023-11-29

Cond是什么?

Cond是Go中sync包的一个结构体类型,它提供了一种基于条件变量的线程同步机制,可以用来等待或通知Goroutine。

Cond类型的定义如下:

type Cond struct {
    L Locker
    // contains filtered or unexported fields
}

其中L是接口Locker,可以是任何类型满足一定接口。Locker接口只有两个方法:Lock和Unlock,代表着锁定和解锁操作。

Cond的使用场景

Cond通常用于需要线程同步的场景,例如多个Goroutine需要等待某个事件(或共享资源)完成后再进行操作。例如,如果一个计算任务需要输入数据进行计算,那么需要先确保数据已经就绪,然后再启动多个Goroutine进行计算。

下面是一个使用Cond实现的例子:多个Goroutine同时等待一个资源调度:

package main

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

func main() {
    var wg sync.WaitGroup
    var m sync.Mutex
    cond := sync.NewCond(&m)
    ready := false
    
    //启动5个Goroutine等待调度
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            m.Lock()
            for !ready {
                cond.Wait()
            }
            fmt.Printf("Goroutine %d do something...\n", id)
            m.Unlock()
        }(i)
    }
    
    //模拟资源调度
    time.Sleep(time.Second)
    m.Lock()
    ready = true
    cond.Broadcast()
    m.Unlock()
    
    //等待所有Goroutine结束
    wg.Wait()
}

这段代码中,启动了5个Goroutine,在调度前一直处于等待状态,调度完成后才开始执行。其中Wait方法会在等待前自动进行解锁操作,避免了死锁。

Cond的方法

Cond提供了三个方法:

  • Wait:等待条件变量满足。
  • Broadcast:广播通知所有等待者条件变量已经满足,由此唤醒所有等待者。
  • Signal:通知任意一个等待者条件变量已经满足,由此唤醒一个等待者。

三个方法都需要在加锁状态下使用。

下面是实现一个生产者-消费者模型的例子,演示了Wait和Signal的使用:

package main

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

func main() {
    var wg sync.WaitGroup
    var m sync.Mutex
    cond := sync.NewCond(&m)
    queue := make([]int, 0, 10)
    
    //启动5个生产者
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            for {
                //生产一个随机数
                num := int(time.Now().UnixNano() % 100)
                
                m.Lock()
                //如果队列满了,等待消费者处理
                for len(queue) == cap(queue) {
                    cond.Wait()
                }
                
                //将数据放入队列
                queue = append(queue, num)
                fmt.Printf("Producer %d produce %d, queue: %v\n", id, num, queue)
                
                //通知一个等待的消费者
                cond.Signal()
                m.Unlock()
                
                time.Sleep(time.Millisecond * 500)
            }
        }(i)
    }
    
    //启动1个消费者
    wg.Add(1)
    go func() {
        defer wg.Done()
        for {
            m.Lock()
            //如果队列为空,等待生产者生产
            for len(queue) == 0 {
                cond.Wait()
            }
            //取出队列第一个数据
            num := queue[0]
            queue = queue[1:]
            fmt.Printf("Consumer consume %d, queue: %v\n", num, queue)
            //通知一个等待的生产者
            cond.Signal()
            m.Unlock()
            
            time.Sleep(time.Millisecond * 1000)
        }
    }()
    
    //等待所有Goroutine结束
    wg.Wait()
}

这段代码中,启动了5个生产者和1个消费者,其中生产者会按固定时间随机生成一个整数,并添加到队列中,队列满时会等待消费者处理。消费者会从队列中取出第一个数据进行处理,队列为空时会等待生产者生成新数据。其中Wait和Signal的使用使得生产和消费能够高效地进行。

总结

Cond是Go中一种非常实用的线程同步工具,提供了基于条件变量的线程同步机制,可以用于等待或通知Goroutine。使用Cond可以有效避免死锁和资源浪费,提高程序的效率。使用时需要注意加锁和解锁状态,避免竞争。