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);
}