假設我們現在有這么一個需求:
計算1-200之間各個數的階乘,並將每個結果保存在mao中,最終顯示出來,要求使用goroutime。
分析:
(1)使用goroutime完成,效率高,但是會出現並發/並行安全問題;
(2)不同協程之間如何通信;
- 對於(1):不同協程之間可能同時對一塊內存進行操作,導致數據的混亂,即並發/並行不安全;主協程運行完了,計算階乘的協程卻沒有運行完,功能並不能夠准確實現;可利用互斥鎖解決該問題;
- 對於(2):可以利用利用管道;
正常的代碼:
package main
import (
"fmt"
"sync"
)
var (
myMap = make(map[int]int, 10)
)
func cal(n int) {
res := 1
for i := 1; i <= n; i++ {
res *= i
}
myMap[n] = res
}
func main() {
for i := 1; i <= 15; i++ {
go cal(i)
}
for i, v := range myMap {
fmt.Printf("map[%d]=%d\n", i, v)
}
}
運行結果:

1.利用互斥鎖
package main
import (
"fmt"
"sync"
""
)
var (
myMap = make(map[int]int, 10)
//lock是全局互斥鎖,synchornized
lock sync.Mutex
)
func cal(n int) {
res := 1
for i := 1; i <= n; i++ {
res *= i
}
lock.Lock()
myMap[n] = res
lock.Unlock()
}
func main() {
for i := 1; i <= 15; i++ {
go cal(i)
}
for i, v := range myMap {
fmt.Printf("map[%d]=%d\n", i, v)
}
}

有可能主程序運行完了而cal還沒運行完(上面結果只到13,沒有14,15),需要加上time.Sleep(time.Seconde*3),而在輸出時,由於主協程並不知道程序已經完成了,底層仍然可能出現競爭資源,所以在輸出階段也要加上互斥鎖。最終代碼如下:
package main
import (
"fmt"
"sync"
)
var (
myMap = make(map[int]int, 10)
//lock是全局互斥鎖,synchornized
lock sync.Mutex
)
func cal(n int) {
res := 1
for i := 1; i <= n; i++ {
res *= i
}
lock.Lock()
myMap[n] = res
lock.Unlock()
}
func main() {
for i := 1; i <= 15; i++ {
go cal(i)
}
time.Sleep(time.Second * 4)
lock.Lock()
for i, v := range myMap {
fmt.Printf("map[%d]=%d\n", i, v)
}
lock.Unlock()
}
為什么需要管道?
(1)主線程在等待所有協程全部完成的時間很難確定;
(2)如果主線程休眠時間長了,會加長等待時間,如果等待時間短了,可能協程還處於工作狀態,這時也會隨着主協程的結束而銷毀;
(3)通過全局變量加鎖同步來實現通訊,也並不利於多個協程對全局變量的讀寫操作;
管道的介紹:
(1)管道的本質就是一種數據結構--隊列;
(2)數據先進先出;
(3)線程安全,多協程訪問時,不需要加鎖;
(4)管道只能存儲相同的數據類型;
管道的聲明:
var intChan chan int;
var stringChan chan string;
var mapChan chan map[int]string;
var perChan chan Person;
var perChan chan *Person;
注意:管道是引用類型;管道必須初始化后才能寫入數據;管道是有類型的,即IntChan只能寫入int;
管道初始化:
var intChan chan int
intChan = make(chan int,10)
向管道中讀寫數據:
num := 10
intChan<-num
var num2 int
num2<-intChan
注意:管道容量滿了則不能繼續寫入,在沒有使用協程的情況下,管道空了不能繼續讀取。
如何使管道中存儲任意數據類型?

轉載:https://www.cnblogs.com/xiximayou/p/11952988.html

