golang 中的互斥鎖和管道
-
sync 包下的互斥鎖
-
場景:當多個協程對共享變量執行寫操作的時候就會發生資源競爭,需要用互斥鎖。
-
特點:
-
互斥鎖必須是全局的,即所有協程共用同一把互斥鎖
-
在需要寫出執行的代碼中對共享變量的寫操作加鎖。
package main
import (
"fmt"
"sync"
"time"
)
// 需求:現在要計算 1-200 的各個數的階乘,並且把各個數的階乘放入到map中。
// 最後顯示出來。要求使用goroutine完成
// 思路
// 1. 編寫一個函數,來計算各個數的階乘,並放入到 map中.
// 2. 我們啓動的協程多個,統計的將結果放入到 map中
// 3. map 應該做出一個全局的.
var (
myMap = make(map[int]int, 10)
//聲明一個全局的互斥鎖
//lock 是一個全局的互斥鎖,
//sync 是包: synchornized 同步
//Mutex : 是互斥
lock sync.Mutex
)
// test 函數就是計算 n!, 讓將這個結果放入到 myMap
func test(n int) {
res := 1
for i := 1; i <= n; i++ {
res *= i
}
//這裏我們將 res 放入到myMap
//加鎖
lock.Lock()
myMap[n] = res
//解鎖
lock.Unlock()
}
func main() {
// 我們這裏開啓多個協程完成這個任務[20個]
for i := 1; i <= 20; i++ {
go test(i)
}
//休眠10秒鐘【第二個問題 】
time.Sleep(time.Second * 5)
//這裏我們輸出結果,變量這個結果
lock.Lock()
for i, v := range myMap {
fmt.Printf("map[%d]=%d\n", i, v)
}
lock.Unlock()
}
-
管程 channel
-
管道是一個隊列的數據結構,數據遵循了先進先出的原則
-
管道本身是線程安全的,即多個協程操作同一個管道的時候不需要對管道進行加鎖。
-
管道是有類型的,即一個 string 類型的管道只能放 string 類型的數據
-
管道是引用數據類型,即必須 make 以後才能使用,make 的時候需要指定管道的容量,這個容量是不會改變的,後續最多也只能向這個管道中放入指定容量的數據,多了會 deadlock 死鎖。
-
管道在 close 關閉以後就不能向管道中寫入數據但依舊可以從管道中讀取數據
-
管道的遍歷只能使用 for-range 遍歷
-
在 for-range 遍歷的時候如果管道沒有關閉會出現 deadlock 死鎖問題。
-
在 for-range 遍歷的時候如果管道關閉了,則正常遍歷結束。
-
因此遍歷管道的注意事項: 1、關閉管道 2、 用 for-range 遍歷
type Cat struct {
Name string
Age int
}
func main() {
//定義一個存放任意數據類型的管道 3個數據
var allChan chan interface{}
allChan = make(chan interface{}, 3)
// 向管道中放入數據
allChan <- 10
allChan <- "tom jack"
cat := Cat{"小花貓", 4}
allChan <- cat
//從管道中獲取數據
<-allChan
<-allChan
newCat := <-allChan //從管道中取出的Cat是什麼?
fmt.Printf("newCat=%T , newCat=%v\n", newCat, newCat)
//使用類型斷言,將interface{}類型轉爲Cat類型
a := newCat.(Cat)
fmt.Printf("newCat., a.Name)
}
func main() {
intChan := make(chan int, 3)
intChan <- 100
intChan <- 200
close(intChan) // close
//這是不能夠再寫入數到channel
//intChan<- 300
fmt.Println("okook~")
//當管道關閉後,讀取數據是可以的
n1 := <-intChan
fmt.Println("n1=", n1)
//遍歷管道
intChan2 := make(chan int, 100)
for i := 0; i < 100; i++ {
intChan2 <- i * 2 //放入100個數據到管道
}
//在遍歷時,如果channel沒有關閉,則會出現deadlock的錯誤
//在遍歷時,如果channel已經關閉,則會正常遍歷數據,遍歷完後,就會退出遍歷
close(intChan2)
for v := range intChan2 {
fmt.Println("v=", v)
}
}
-
管道實現多協程之間的通信設計
-
由於主線程推出以後,基於主線程環境創建的協程也會結束,因此必須有一個通信機制使得主線程在其他協程執行的時候阻塞。
//write Data
func writeData(intChan chan int) {
for i := 1; i <= 50; i++ {
//放入數據
intChan <- i //
fmt.Println("writeData ", i)
}
close(intChan) //關閉
}
//read data
func readData(intChan chan int, exitChan chan bool) {
for {
v, ok := <-intChan
if !ok {
break
}
time.Sleep(time.Second)
fmt.Printf("readData 讀到數據=%v\n", v)
}
exitChan <- true
close(exitChan)
}
func main() {
//創建兩個管道
intChan := make(chan int, 10)
exitChan := make(chan bool, 1)
go writeData(intChan)
go readData(intChan, exitChan)
for {
_, ok := <-exitChan
if !ok {
break
}
}
}
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/5_O6jmudmMAGe2810jy5mg