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

此篇文章的評論功能已經停用。