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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章