天天看點

go中channel和goroutine以及select的配合使用

channel的建立和關閉:

ch:=make(chan int) ,初始化一個int類型的channel

使用Go内置函數close來關閉一個channel, close(ch)

注意:

1.向一個已經關閉的channel發送消息會程式會panic

2.關閉一個未初始化的channel會panic

3.可以從一個已經關閉的channel中讀取消息,能夠讀取channel中未被讀取的消息,如果所有消息均被讀出,則會讀到類型的零值。從一個已經關閉的channel中讀取消息永遠不會阻塞,ok,idiom:=<-ch, 如果ok為真則未關閉,false則已經關閉。

4.channel關閉時候,所有的goroutine都會受到消息

無緩存的channel

從無緩存的channel中讀取消息會阻塞,直到有goroutine向此channel中發送消息,向無緩存的channel中發消息也會阻塞,直到有goroutine向該channel中讀取消息。

執行個體代碼:

//讀資料
var ch chan int
ch = make(chan int)
//close(ch)

go func() {
	fmt.Println("A")
	_ = <-ch //阻塞在這裡,不會列印B
	fmt.Println("B")
}()

time.Sleep(time.Second * time.Duration(5))



//存資料
var ch chan int
ch = make(chan int)
//close(ch)

go func() {
	fmt.Println("A")
	ch <- 8 //阻塞在這裡,不會列印B
	fmt.Println("B")
}()
           

有緩存的channel

有緩存的 channel 類似一個阻塞隊列(采用環形數組實作)。當緩存未滿時,向 channel 中發送消息時不會阻塞,當緩存滿時,發送操作将被阻塞,直到有其他 goroutine 從中讀取消息;相應的,當 channel 中消息不為空時,讀取消息不會出現阻塞,當 channel 為空時,讀取操作會造成阻塞,直到有 goroutine 向 channel 中寫入消息

make函數的第二個參數來指定緩存的容量

執行個體代碼:

var ch chan int
ch = make(chan int,1)
//close(ch)

go func() {
	fmt.Println("A")
	_ = <-ch //阻塞,盡管有緩存,但是channel是空的沒有放入消息
	fmt.Println(”B”)
     _ = <-ch //阻塞,不會列印C
	fmt.Println("C")

}()

time.Sleep(time.Second * time.Duration(5))
           

channel的周遊

注意:channel可以用range取值,并且一直會從channel中取值,直到有goroutine把channel關閉循環才會結束。

示例代碼:

var ch chan int
ch = make(chan int, 0)

go func() {
	for v := range ch { //循環直到有goroutine close(ch)
		fmt.Println(v)
	}
}()
ch <- 1
ch <- 2
ch <- 3
time.Sleep(time.Second * 5) //睡眠5
ch <- 4
ch <- 5
close(ch)
           

輸出:12345,其中45是在main函數睡眠5秒後輸出,range一直在持續,直到close(ch)

也可以使用 :

for {

      c, ok := <-ch

      if !ok {

         break

      }

}

這種方式來周遊。

select和channel的配合使用

注意:

1.select可以同時監聽多個channel的寫入或讀取

2.執行select若隻有一個case不阻塞,則執行這個case塊

3.若多個case不阻塞,則随機挑選一個執行

4.若所有case都阻塞則實行default塊,若未定義default則select語句阻塞

直到有case被喚醒

5.使用break跳出select

示例代碼:

ch1 := make(chan int)
ch2 := make(chan int)
ch3 := make(chan int)
ch4 := make(chan int)

select {
case <-ch1:
	fmt.Println("A")
case <-ch2:
	fmt.Println("B")
case <-ch3:
	fmt.Println("C")
case <-ch4:
	fmt.Println("D")
default:
	fmt.Println("E") //全部阻塞,執行default
}

fmt.Println("F")
           

示例代碼2:

ch1 := make(chan int)
ch2 := make(chan int)
ch3 := make(chan int)
ch4 := make(chan int)

go func() {
	fmt.Println("1")
	ch1 <- 1
	fmt.Println("2")
	ch2 <- 2
	fmt.Println("3")
	ch3 <- 3

}()
time.Sleep(time.Second * 1)
select {
case <-ch1:
	fmt.Println("A")
case <-ch2:
	fmt.Println("B")
case <-ch3:
	fmt.Println("C")
case <-ch4:
	fmt.Println("D")
default:
	//fmt.Println("E") //全部阻塞,執行default
}

fmt.Println("F")

//輸出: 1 
//     A
//     F
//     2
           

    首先執行goroutine 會列印1 此時給ch1指派 會阻塞,1秒後當執行select 則case ch1通過列印A,此時 goroutine 被喚醒 同時main函數也會繼續執行 則列印F、2,main函數退出 .