内容简介:only_read := make(<-chan int,1) //只能发送通道only_write := make(chan<- int, 1) //只能接受通道单向通道的主要用途就是约束其他代码的行为。
单向通道
only_read := make(<-chan int,1) //只能发送通道
only_write := make(chan<- int, 1) //只能接受通道
问:单向通道的用途?
答:
单向通道的主要用途就是约束其他代码的行为。
func SendInt(ch chan<- int) { ch <- rand.Intn(1000) }
在实际场景中,这种约束一般会出现在接口类型声明中的某个方法定义上。 demo:
type Notifier interface{ SendInt(ch chan<- int) }
在这里,Notifier接口中的SendInt方法只会接受一个发送通道作为参数,所以,在该接口中的所有实现类型中的SendInt 方法都会受到限制。这种约束方法还是很有用的,尤其是在我们编写模版代码或者可扩展程序库的时候。
顺便说一下,我们在调用SendInt函数的时候,只需要把一个元素类型匹配的双向通道传给它就行了。没必要用发送通道,因为Go 语言在这种情况下自动地把双向通道转换为函数所需的单向通道。
intChan1 := make(chan int, 3) SendInt(intChan1)
在另一方面,我们还可以在函数声明的结果列表中使用单向通道。如下所示:
func getIntChan() <-chan int { num := 5 ch := make(chan int, num) for i := 0; i< num; i++{ ch <- i } close(ch) return ch }
函数getIntChane会返回一个 <-chan int 类型的通道,这就意味着得到该通道的程序,只能从通道中接受元素值。这实际上就是对函数调用方的一种约束了。
另外。我们在Go 语言中还可以声明函数类型,如果我们在函数类型中使用了单向通道,那么就相当于在约束所有实现了这个函数类型的函数。
intChan2 := getIntChane() for elem := range intChan2{ fmt.Printf("The element int intChan2: %v\n", elem) }
for range 遍历通道要注意的点:
1.for 语句会不断尝试从intChan2 中取出元素值,即使intChan2被关闭,它也会在取出所有剩余的元素值之后再结束关闭。
2.当 intChan2中没有元素值时,它会被阻塞在有 for关键字的那一行,直捣有新的元素可取。
3.假设intChan2的值为nil,那么它会被永远阻塞在有for关键字的那一行。
问:select语句与通道联用,应注意什么?
答:
// 通道数组 intChannels := [3]chan int { make(chan int, 1), make(chan int, 1), make(chan int, 1), } // 随机选择一个通道,并向它发送元素子 index := rand.Int(3) fmt.Printf("The index:%d \n",index) intChannels[index] <-index // 哪一个通道中有可取的元素值,哪个对应的分支就被执行。 select { case<-intChannels[0]: fmt.Println("The first candidate case is selected.") case<-intChannels[1]: fmt.Println("The second candidate case is selected.") case<-intChannels[2]: fmt.Println("The third candidate case is selected.") default: fmt.Println("No candidate case is selected") }
在使用select语句时,应该注意:
1. 如果加入了default分支,那么无论涉及通道操作的表达式是否有阻塞,select语句都不会被阻塞。如果那几个表达式被阻塞了,或者说都没有满足求值的条件,那么默认分支就会被选中并执行。
2. 如果没有加入默认分支,那么一旦所有的case表达式都没有满足求值条件,那么select语句就会被阻塞。直到至少有一个case表达式满足条件为止。
3. 我们可能会因为通道关闭了,而直接从通道接收到一个其元素类型的零值。所以,在很多时候,我们需要通过接受表达式的第二个结果值来判断通道是否关闭。一旦发现某个通道关闭了,我们就应该及时地屏蔽掉对应的分支或者采取其他措施。这对于程序逻辑和程序性能都是有好处的。
4. select语句只能对其中的一个每一个case表达式各求值一次。所以,如果我们想连续或定时地操作其中的通道时,就往往需要在for语句中嵌入select语句的方法实现。但这时要注意,简单地在select语句的分支中使用break语句,只能结束当前的select语句的执行,而不会对外层的for语句产生作用。这种错误的用法可能会让这个for语句无休止地运行下去。
错误示例: intChan := make(chan int, 1) // 1秒钟后关闭通道 time.AfterFunc(time.Second, func(){ close(intChan) }) select { case _, ok := <-intChan: if !ok { break;//错误,不能跳出for,只能跳出select } fmt.Println("The candidate case is selected.") }
select分支选择规则细节:
1. select语句候选分支的case表达式 求值顺序是依次从代码的编写顺序从上到下的。排在最上方的候选分支中最左边的表达式会最先被求值,然后是它右边的表达式。然后依次顺序向下的候选分支开始求值。
2. 进当select语句中的所有case表达式都被求值完毕后,它才会开始选择候选分支。这时候,它只会挑选满足选择条件的候选分支执行。如果所有的候选分支都不满足条件,那么默认分支就会被执行。如果这时没有默认分支,那么select分支语句就会立即进入阻塞状态,直到至少有一个候选分支满足选择条件为止。一旦有一个候选分支满足选择条件,select语句(或者说它所在的goroutine)就会被唤醒,这个候选分支就会被执行。
3. 如果select语句发现同时有多个候选分支满足选择条件,那么它会用一个伪随机的算法在这些分支中选择一个并执行。注意,即使select语句是在被唤醒时发现的这种情况,也会这么做。
4. select语句的每次执行,包括case表达式求值和分支选择,都是独立的,不过,至于它的执行是否是并发安全的,就要看其中的case表达式以及分支中,是否包含并发不安全的代码了。
问题:1. 如果在select语句中发现某个通道已关闭,那么应该怎样屏蔽它所在的分支?
回答:当判断表达式的第二个参数为false时,代表通道已经关闭,这时候将该channel赋值为nil,那么每次从该nil channel接受时就会阻塞,select会忽略阻塞的通道。
问题:2.在select语句与for语句联用时,怎么直接退出最外层的for语句?
回答:通过定义标签,配合goto或者breal语句能实现在同一个函数内任意跳转,就可以跳出多层循环了。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Golang 并发,有缓存通道,通道同步案例演示
- 科普 | 菜鸟学习状态通道,Part-2:App 定制型状态通道
- golang通道定义
- 通道 | Java NIO
- Go 通道
- golang 通道channel 基础
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。