From dab7a34ab6865bacfe1401a05ab703d20712af07 Mon Sep 17 00:00:00 2001 From: glight2000 <173959153@qq.com> Date: Tue, 29 Dec 2015 11:24:27 +0800 Subject: [PATCH] Update 14.3.md --- eBook/14.3.md | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/eBook/14.3.md b/eBook/14.3.md index 4020dbe..b3d12fc 100644 --- a/eBook/14.3.md +++ b/eBook/14.3.md @@ -1 +1,110 @@ # 协程的同步:关闭通道-测试阻塞的通道 + +通道可以被显示的关闭;尽管它们和文件不同:不必每次都关闭。只有在当需要告诉接收者不会再提供新的值的时候,才需要关闭通道。只有发送者需要关闭通道,接收者永远不会需要。 + +继续看示例[goroutine2.go](examples/chapter_14/goroutine2.go)(示例14.2):我们如何在通道的`sendData()`完成的时候发送一个信号,`getData()`又如何检测到通道是否关闭或阻塞? + +第一个可以通过函数`close(ch)`来完成:这个将通道标记为无法通过发送操作<-接受更多的值;给已经关闭的通道发送或者再次关闭都会导致运行时的panic。在创建一个通道后使用defer语句是个不错的办法(类似这种情况): +```go +ch := make(chan float64) +defer close(ch) +``` +第二个问题可以使用逗号,ok操作符:用来检测通道是否被关闭。 + +如何来检测可以收到没有被阻塞(或者通道没有被关闭)? +```go +v, ok := <-ch // ok is true if v received value +``` +通常和if语句一起使用: +```go +if v, ok := <-ch; ok { + process(v) +} +``` +或者在for循环中接收的时候,当关闭或者阻塞的时候使用break: +```go +v, ok := <-ch +if !ok { + break +} +process(v) +``` +可以通过`_ = ch <- v`来实现非阻塞发送,因为空标识符获取到了发送给`ch`的任何东西。在示例程序14.2中使用这些可以改进为版本goroutine3.go,输出相同。 + +实现非阻塞通道的读取,需要使用select(参见章节[14.4](14.4.md)) + +示例 14.9-[goroutine3.go](examples/chapter_14/goroutine3.go) +```go +package main + +import "fmt" + +func main() { + ch := make(chan string) + go sendData(ch) + getData(ch) +} + +func sendData(ch chan string) { + ch <- "Washington" + ch <- "Tripoli" + ch <- "London" + ch <- "Beijing" + ch <- "Tokio" + close(ch) +} + +func getData(ch chan string) { + for { + input, open := <-ch + if !open { + break + } + fmt.Printf("%s ", input) + } +} +``` +改变了以下代码: +* 现在只有`sendData()`是协程,`getData()`和`main()`在同一个线程中: +```go +go sendData(ch) +getData(ch) +``` +* 在`sendData()`函数的最后,关闭了通道: +```go +func sendData(ch chan string) { + ch <- "Washington" + ch <- "Tripoli" + ch <- "London" + ch <- "Beijing" + ch <- "Tokio" + close(ch) +} +``` +* 在for循环的`getData()`中,在每次接收通道的数据之前都使用`if !open`来检测: +```go +for { + input, open := <-ch + if !open { + break + } + fmt.Printf("%s ", input) + } +``` +使用for-range语句来读取通道是更好的办法,因为这会自动检测通道是否关闭: +```go +for input := range ch { + process(input) +} +``` +阻塞和生产者-消费者模式: + + + + + +## 链接 + +- [目录](directory.md) +- 上一节:[协程间的信道](14.2.md) +- 下一节:[使用select切换协程](14.4.md)