天天看点

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函数退出 .