channel通道
尚硅谷Golang課
channel通道
lock sync與chan基本認識
//階乘1-n 把各個數放到map中 用goroutine完成
var (
myMap = make(map[int]int, 10)
//聲明一個全局的互斥鎖
lock sync.Mutex
//來點管道 記得要make初始化才能用
intChan chan int = make(chan int, 3)
//聲明類型為空接口可以接收任意類型
allChan chan interface{} = make(chan interface{}, 5)
//注意後面沒聲明容量則不給用
)
type Cat struct {
Name string
Age int
}
func test(n int) {
res := 1
for i := 1; i <= n; i++ {
res = res * i
}
//把res放進mayMap
//加鎖
lock.Lock()
myMap[n] = res
//解鎖
lock.Unlock()
}
func main() {
//啟用多個協程
for i := 1; i <= 1; i++ {
go test(i)
}
//如果不加鎖concurrent map iteration and map write
lock.Lock()
for i, v := range myMap {
fmt.Printf("map[%d]=%v\n", i, v)
}
lock.Unlock()
//看看管道
fmt.Println(intChan)
//出來是一個地址,表示誰要經過這 (當然本身也有一個地址但不重要)
fmt.Printf("長度=%v 容量=%v\n", len(intChan), cap(intChan))
//向管道寫入數據
intChan <- 10
fmt.Printf("長度=%v 容量=%v\n", len(intChan), cap(intChan))
num1 := 5
intChan <- num1
intChan <- 1
fmt.Printf("長度=%v 容量=%v\n", len(intChan), cap(intChan))
//intChan <- 12 //超過3個太長報錯
//從管道取數據
var num2 int = <-intChan
fmt.Println(num2)
fmt.Printf("長度=%v 容量=%v\n", len(intChan), cap(intChan))
//先進先出,取完超過一樣報錯
num3 := <-intChan
fmt.Println(num3)
fmt.Printf("長度=%v 容量=%v\n", len(intChan), cap(intChan))
//關於allChan
allChan <- 10
allChan <- "ABC"
cat1 := Cat{"小花", 3}
allChan <- cat1
//可以亂塞一通
//想拿到第三個元素 先把兩個推出來
<-allChan
<-allChan
newCat := <-allChan //從管道中取出來的cat1
fmt.Printf("類型=%T 值=%v\n", newCat, newCat)
// fmt.Printf("%v\n", newCat.Name) //雖然類型一樣但這樣用不通過
//必須類型斷言
a := newCat.(Cat)
fmt.Printf("%v\n", a.Name)
}
只讀或寫
func main() {
//chan可以只讀或只寫
// chan1 chan int //可讀可寫
var chan2 chan<- int = make(chan int, 3) //只寫
chan2 <- 20
// num:=<-chan2//報錯
fmt.Println(chan2)
var chan3 <-chan int = make(chan int, 3)
num2 := <-chan3
fmt.Println(num2)
chan3 <- 10 //報錯
}
遍歷chan
func main() {
//用close關掉channel就不能往內加(但是可以拿出)
var intChan chan int = make(chan int, 3)
intChan <- 100
intChan <- 200
close(intChan)
// intChan <- 300 //錯誤
n1 := <-intChan
fmt.Println(n1)
//遍歷
intChan2 := make(chan int, 100)
for i := 0; i < 100; i++ {
intChan2 <- i * 2 //放100個數據(2,4,6...進去)
}
//得用for:=range用一般for遍歷會報錯
//如果已經close遍歷完就會自動停止,如果沒有先關掉會deadlock
close(intChan2)
for v := range intChan2 {
fmt.Println(v)
}
}
阻塞
func writeData(intChan chan int) {
for i := 1; i <= 50; i++ {
intChan <- i
fmt.Printf("writeData=%v\n", i)
// time.Sleep(time.Second / 1000)
}
close(intChan) //寫完就關
}
func readData(intChan chan int, exitChan chan bool) {
for {
v, ok := <-intChan
// time.Sleep(time.Second / 1000)
if !ok {
break
}
fmt.Printf("readData讀取到數據=%v\n", v)
}
//讀取完=任務完成 其實這裡不管對exitChan做啥都無所謂,重點是有調用他就能阻塞main
exitChan <- true
close(exitChan)
}
func main() {
intChan := make(chan int, 5)
//即使管道小 寫的慢讀得快或相反,他會排隊,重點不要阻塞死鎖就能繼續
exitChan := make(chan bool, 1)
go writeData(intChan)
go readData(intChan, exitChan)
if <-exitChan { //重點就是這個判斷調用到一個管道有跨到子函數的就能阻塞住main
fmt.Println("任務完成")
}
}
用select解決阻塞
func sayH() {
for i := 0; i < 10; i++ {
time.Sleep(time.Second / 10)
fmt.Println("hello")
}
}
func test() {
defer func() {
if err := recover(); err != nil {
fmt.Println("test錯誤", err)
}
}()
myMap := make(map[int]string)
myMap[0] = "我家"
fmt.Println(myMap)
}
func main() {
go sayH()
go test()
fmt.Println("hellom")
intChan := make(chan int, 10)
for i := 0; i < 10; i++ {
intChan <- i
}
stringChan := make(chan string, 5)
for i := 0; i < 5; i++ {
stringChan <- "hello" + fmt.Sprintf("%d", i)
}
//傳統方法遍歷,如果不關閉會阻塞 就導致deadlock
//實際開發不好確定哪時候關閉,就用select解決
for {
select {
//就算沒關閉,只要阻塞他就往其他case移動
case v := <-intChan:
fmt.Printf("intChan取出%d\n", v)
case v := <-stringChan:
fmt.Printf("stringChan取出%s\n", v)
default:
fmt.Println("取完了")
return
}
}
}
上次修改於 2021-08-01
此篇文章的評論功能已經停用。