Go 学习笔记12.once使用 cond条件变量控制 生产者消费者模型

Go语言特色编程

1.并发编程

1.1 sync.WaitGroup

类似于信号量

Add(num) 加 num;

Done() 减一;

Wait() 阻塞,一直到减到0时为止

package main

import (
	"fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(10)
    for i :=0;i<10;i++ {
        // 执行匿名函数,协程
        go func(num int){
            fmt.Println("I am ",num,"goroutine!")
            wg.Done()
        }(i)
    }
    
    wg.Wait()
    fmp.Println("----- over ------")
}

1.2 互斥锁 与 读写锁

互斥锁

var mutex sync.Mutex

mutex.Lock() //上锁 , 解锁也可以使用defer mutex.Unlock()

mutex.Unlock() // 解锁

读写锁

mutex sync.RWMutex

正常锁

mutex.Lock()

mutex.Unlock()

读锁

mutex.RLock()

mutex.RUnlock()

教程

https://blog.csdn.net/sunt2018/article/details/105608184

1.3 控制代码只在go协程中运行一次

sync.Once -> Do( 代码或函数)

package main

import (
	"fmt"
	"sync"
)

func main() {

	var wg sync.WaitGroup
	var once sync.Once
	var count int = 1

	wg.Add(100)
	for i := 0; i<100 ;i++ {
		go func() {
			once.Do(func() {
				fmt.Println("add...count")
				count += 1
			})
			wg.Done() // 减去1的操作
		}()

	}

	wg.Wait() // 阻塞等待
	fmt.Println(count)
}

1.4 Cond 条件变量实现生产者消费者模型

例子是 一个厨师(生产者),多个顾客(消费者)

厨师不停的生成,顾客不停的吃

有一个容量为5的桌子,厨师将生产出来的东西放到桌子上每次+1,消费者吃每次-1

临界区: 桌子的容量是5,食物的上限是5个,超过厨师不能再放食物,下限是0个,超过顾客没有东西吃

是用cond条件变量 信号量,

当食物为5,厨师cond.Wait()等待,顾客吃掉一个后,cond.Signal()唤醒厨子去做饭

其中 Wait函数使用前,要加锁,使用后解锁。 因为wait内部实现是 先解锁,阻塞住等待通知,接到通知后再加锁。

package main

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

var wg sync.WaitGroup
// 做一个临界区,环形队列 [0 1 2 3 4]
var five [5]int // 共享队列
var cond *sync.Cond // 条件变量
var mutex sync.Mutex // 锁

// 控制生产者生产数量 最多5个
var cond_five *sync.Cond // 条件变量
var mutex_five sync.Mutex // 锁
var prod_count int = 0

var customer_index int = 0

// 生产者
func productor() {
	index := 0 // 生产下标
	prodnum := 1000 //产品编号
	for {
		// 抢锁
		mutex.Lock()
		// 生产商品
		time.Sleep(time.Millisecond * 100)
		five[index] = prodnum // 模拟生成商品
		fmt.Println("I am productor,prodnum is",prodnum)
		prodnum++
		prod_count++  //产品数量增加
		index = (index + 1) % 5 // 环形队列
		cond.Signal() // 唤醒一个go协程
		// 释放锁
		mutex.Unlock()
		// 判断是否可以继续生成
		if prod_count == 5 {
			mutex_five.Lock()
			cond_five.Wait()
			mutex_five.Unlock()
		}
	}
	wg.Done()

}

// 消费者
func customer(nu int) {

	for {
		// 抢锁
		mutex.Lock()
		// wait
		cond.Wait() // 多个go协程可能阻塞等待条件发生
		// 消费
		if prod_count > 0 {
			time.Sleep(time.Millisecond * 10)
			fmt.Printf("I am %d customer,prodnum is %d\n",nu,five[customer_index])
			customer_index = (customer_index + 1) % 5
			prod_count--
		}
		// 释放锁
		mutex.Unlock()
		cond_five.Signal() //通知生产者

	}
	wg.Done()

}

func main() {
	cond = sync.NewCond(&mutex) // 构造条件变量
	cond_five = sync.NewCond(&mutex_five)
	wg.Add(2)
	go productor()
	go customer(1)
	go customer(2)
	wg.Wait()

}

1.5 channel通信与CSP并发模型

这个就是go的管道

Go 学习笔记08.协程和管道
https://blog.csdn.net/sunt2018/article/details/105585772

Go 学习笔记09.管道 常见操作
https://blog.csdn.net/sunt2018/article/details/105606943
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章