14.3-14.4

This commit is contained in:
Unknwon
2016-01-05 15:03:32 +08:00
parent 5e06368e45
commit 7ccbf3915f
5 changed files with 83 additions and 54 deletions

View File

@@ -2,26 +2,33 @@
通道可以被显示的关闭;尽管它们和文件不同:不必每次都关闭。只有在当需要告诉接收者不会再提供新的值的时候,才需要关闭通道。只有发送者需要关闭通道,接收者永远不会需要。
继续看示例[goroutine2.go](examples/chapter_14/goroutine2.go)示例14.2):我们如何在通道的`sendData()`完成的时候发送一个信号,`getData()`又如何检测到通道是否关闭或阻塞?
继续看示例 [goroutine2.go](examples/chapter_14/goroutine2.go)(示例 14.2):我们如何在通道的 `sendData()` 完成的时候发送一个信号,`getData()` 又如何检测到通道是否关闭或阻塞?
第一个可以通过函数 `close(ch)` 来完成:这个将通道标记为无法通过发送操作 `<-` 接受更多的值;给已经关闭的通道发送或者再次关闭都会导致运行时的 panic。在创建一个通道后使用 defer 语句是个不错的办法(类似这种情况):
第一个可以通过函数`close(ch)`来完成:这个将通道标记为无法通过发送操作<-接受更多的值给已经关闭的通道发送或者再次关闭都会导致运行时的panic在创建一个通道后使用defer语句是个不错的办法类似这种情况
```go
ch := make(chan float64)
defer close(ch)
```
第二个问题可以使用逗号ok操作符用来检测通道是否被关闭
第二个问题可以使用逗号ok 操作符:用来检测通道是否被关闭。
如何来检测可以收到没有被阻塞(或者通道没有被关闭)?
```go
v, ok := <-ch // ok is true if v received value
```
通常和if语句一起使用
通常和 if 语句一起使用:
```go
if v, ok := <-ch; ok {
process(v)
}
```
或者在for循环中接收的时候当关闭或者阻塞的时候使用break
或者在 for 循环中接收的时候,当关闭或者阻塞的时候使用 break
```go
v, ok := <-ch
if !ok {
@@ -29,11 +36,13 @@ if !ok {
}
process(v)
```
可以通过`_ = ch <- v`来实现非阻塞发送因为空标识符获取到了发送给`ch`的任何东西在示例程序14.2中使用这些可以改进为版本goroutine3.go输出相同
实现非阻塞通道的读取需要使用select参见章节[14.4](14.4.md)
可以通过 `_ = ch <- v` 来实现非阻塞发送,因为空标识符获取到了发送给 `ch` 的任何东西。在示例程序 14.2 中使用这些可以改进为版本 goroutine3.go输出相同。
实现非阻塞通道的读取,需要使用 select参见第 [14.4](14.4.md) 节)。
示例 14.9-[goroutine3.go](examples/chapter_14/goroutine3.go)
示例 14.9-[goroutine3.go](examples/chapter_14/goroutine3.go)
```go
package main
@@ -64,13 +73,18 @@ func getData(ch chan string) {
}
}
```
改变了以下代码:
* 现在只有`sendData()`是协程`getData()``main()`在同一个线程中
- 现在只有 `sendData()` 是协程,`getData()``main()` 在同一个线程中:
```go
go sendData(ch)
getData(ch)
```
* `sendData()`函数的最后关闭了通道
-`sendData()` 函数的最后,关闭了通道:
```go
func sendData(ch chan string) {
ch <- "Washington"
@@ -81,7 +95,9 @@ func sendData(ch chan string) {
close(ch)
}
```
* 在for循环的`getData()`在每次接收通道的数据之前都使用`if !open`来检测
- 在 for 循环的 `getData()` 中,在每次接收通道的数据之前都使用 `if !open` 来检测:
```go
for {
input, open := <-ch
@@ -91,15 +107,18 @@ for {
fmt.Printf("%s ", input)
}
```
使用for-range语句来读取通道是更好的办法因为这会自动检测通道是否关闭
使用 for-range 语句来读取通道是更好的办法,因为这会自动检测通道是否关闭:
```go
for input := range ch {
process(input)
}
```
阻塞和生产者-消费者模式:
在章节14.2.10的通道迭代器中两个协程经常是一个阻塞另外一个如果程序工作在多核心的机器上大部分时间只用到了一个处理器可以通过使用带缓冲缓冲空间大于0的通道来改善比如缓冲大小为100迭代器在阻塞之前至少可以从容器获得100个元素如果消费者协程在独立的内核运行就有可能让协程不会出现阻塞
在第 14.2.10 节的通道迭代器中两个协程经常是一个阻塞另外一个如果程序工作在多核心的机器上大部分时间只用到了一个处理器可以通过使用带缓冲缓冲空间大于 0的通道来改善。比如缓冲大小为 100迭代器在阻塞之前至少可以从容器获得 100 个元素如果消费者协程在独立的内核运行就有可能让协程不会出现阻塞
由于容器中元素的数量通常是已知的,需要让通道有足够的容量放置所有的元素。这样,迭代器就不会阻塞(尽管消费者协程仍然可能阻塞)。然后,这样有效的加倍了迭代容器所需要的内存使用量,所以通道的容量需要限制一下最大值。记录运行时间和性能测试可以帮助你找到最小的缓存容量带来最好的性能。
@@ -107,4 +126,4 @@ for input := range ch {
- [目录](directory.md)
- 上一节:[协程间的信道](14.2.md)
- 下一节[使用select切换协程](14.4.md)
- 下一节[使用 select 切换协程](14.4.md)