1. 程式人生 > >go語言sync包的學習(Mutex、WaitGroup、Cond)

go語言sync包的學習(Mutex、WaitGroup、Cond)

pri lee 拷貝 light 等待 runt broadcast 計算 混亂

package main;

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

//加鎖,註意鎖要以指針的形式傳進來,不然只是拷貝
func total1(num *int, mu *sync.Mutex, ch chan bool) {
	mu.Lock();
	for i := 0; i < 1000; i++ {
		*num += i;
	}
	ch <- true;
	mu.Unlock();
}

//不加鎖
func total2(num *int, ch chan bool) {
	for i := 0; i < 1000; i++ {
		*num += i;
	}
	ch <- true;
}

//Lock、Unlock與RLock、RUnlock不能嵌套使用
func total3(num *int, rwmu *sync.RWMutex, ch chan bool) {
	for i := 0; i < 1000; i++ {
		rwmu.Lock();
		*num += i;
		rwmu.Unlock();

		if(i == 500) {
			//讀鎖定
			rwmu.RLock();
			fmt.Print(*num, " ");
			rwmu.RUnlock();
		}
	}
	ch <- true;
}

func printNum(num int, cond *sync.Cond) {
	cond.L.Lock();
	if num < 5 {
		//num小於5時,進入等待狀態
		cond.Wait();
	}
	//大於5的正常輸出
	fmt.Println(num);
	cond.L.Unlock();
}

func main() {
	//Once.Do()保證多次調用只執行一次
	once := sync.Once{};
	ch := make(chan bool, 3);
	for i := 0; i < 3; i++ {
		go func(n int) {
			once.Do(func() {
				//只會執行一次,因為閉包引用了變量n,最後的值為2
				fmt.Println(n)
			});
			//給chan發送true,表示執行完成
			ch <- true;
		}(i);
	}
	for i := 0; i < 3; i++ {
		//讀取三次chan,如果上面三次沒執行完會一直阻塞
		<-ch;
	}

	//互斥鎖,保證某一時刻只能有一個訪問對象
	mutex := sync.Mutex{};
	ch2 := make(chan bool, 20);
	//使用多核,不然下面的結果會一樣
	runtime.GOMAXPROCS(runtime.NumCPU());
	num1 := 0;
	num2 := 0;
	for i := 0; i < 10; i++ {
		go total1(&num1, &mutex, ch2);
	}
	for i := 0; i < 10; i++ {
		go total2(&num2, ch2);
	}
	for i := 0; i < 20; i++ {
		<-ch2;
	}
	//會發現num1與num2計算出的結果不一樣
	//而num1的結果才是正確的,因為total2沒有加鎖,導致多個goroutine操作num時發生數據混亂
	fmt.Println(num1, num2);

	//讀寫鎖,多了讀鎖定,和讀解鎖,讓多個goroutine同時讀取對象
	rwmutex := sync.RWMutex{};
	ch3 := make(chan bool, 10);
	num3 := 0;
	for i := 0; i < 10; i++ {
		go total3(&num3, &rwmutex, ch3);
	}
	for i := 0; i < 10; i++ {
		<-ch3;
	}
	fmt.Println(num3);

	//組等待,等待一組goroutine的結束
	wg := sync.WaitGroup{};
	//增加計數器
	wg.Add(10);
	for i:= 0; i< 10; i++ {
		go func(n int) {
			fmt.Print(n, " ");
			//這裏表示該goroutine執行完成
			wg.Done();
		}(i);
	}
	//等待所有線程執行完成
	wg.Wait();

	fmt.Println("");

	//條件等待
	mutex2 := sync.Mutex{};
	//使用鎖創建一個條件等待
	cond := sync.NewCond(&mutex2);

	for i := 0; i < 10; i++ {
		go printNum(i, cond);
	}

	time.Sleep(time.Second * 1);
	//等待一秒後,我們先喚醒一個等待,輸出一個數字
	cond.L.Lock()
	cond.Signal();
	cond.L.Unlock();
	time.Sleep(time.Second * 1);
	//再次待待一秒後,喚醒所有,輸出余下四個數字
	cond.L.Lock()
	cond.Broadcast();
	cond.L.Unlock();
	time.Sleep(time.Second * 1);
}

  

go語言sync包的學習(Mutex、WaitGroup、Cond)