Update 14.2.md

This commit is contained in:
glight2000
2015-12-26 15:28:40 +08:00
parent 7fd3b78990
commit 5095fe05c3

View File

@@ -203,9 +203,111 @@ buf是通道可以承受的元素这里是string个数
在缓冲满载缓冲被全部使用之前给一个带缓冲的通道发送数据是不会阻塞的而从通道读取数据也不会阻塞直到缓冲空了
缓冲容量和类型无关所以可以尽管可能导致危险给一些通道设置不同的容量只要他们拥有同样的元素类型
缓冲容量和类型无关所以可以尽管可能导致危险给一些通道设置不同的容量只要他们拥有同样的元素类型内置的`cap`函数可以返回缓冲区的容量
如果容量大于0通道就是异步的了缓冲满载发送或变空接收之前通信不会阻塞元素会按照发送的顺序被接收如果容量是0或者未设置通信仅在收发双方准备好的情况下才可以成功
同步ch :=make(chan type, value)
value == 0 -> synchronous, unbuffered (阻塞)
value > 0 -> asynchronous, buffered非阻塞取决于value元素
若使用通道的缓冲你的程序会在“请求”激增的时候表现更好更具弹性专业术语叫更具有伸缩性scalable。要在首要位置使用无缓冲通道来设计算法只在不确定的情况下使用缓冲。
练习 14.3[channel_buffer.go](exercises/chapter_14/channel_buffer.go):给[channel_block3.go](exercises/chapter_14/channel_block3.go)的通道增加缓冲并观察输出有何不同。
## 14.2.6 协程中用通道输出结果
为了知道计算何时完成,可以通过信道回报。在例子`go sum(bigArray)`中,要这样写:
```go
ch := make(chan int)
go sum(bigArray, ch) // bigArray puts the calculated sum on ch
// .. do something else for a while
sum := <- ch // wait for, and retrieve the sum
```
也可以使用通道来达到同步的目的这个很有效的用法在传统计算机中成为semaphore。或者换个方式通过通道发送信号告知处理已经完成在协程中
在其他协程运行时让main程序无限阻塞的通常做法是在`main`函数的最后放置一个{}。
也可以使用通道让`main`程序等待协程完成,就是所谓的信号灯模式,我们会在接下来的部分讨论。
## 14.2.7 信号灯模式
下边的片段阐明:协程通过在通道`ch`中放置一个值来处理结束的信号。`main`协程等待`<-ch`直到从中获取到值。
我们期望从这个通道中获取返回的结果,像这样:
```go
func compute(ch chan int){
ch <- someComputation() // when it completes, signal on the channel.
}
func main(){
ch := make(chan int) // allocate a channel.
go compute(ch) // stat something in a goroutines
doSomethingElseForAWhile()
result := <- ch
}
```
这个信号也可以是其他的不反回结果比如下边这个协程中的lambda函数
协程:
```go
ch := make(chan int)
go func(){
// doSomething
ch <- 1 // Send a signal; value does not matter
}
doSomethingElseForAWhile()
<- ch // Wait for goroutine to finish; discard sent value.
```
或者等待两个协程完成每一个都会对切片s的一部分进行排序片段如下
```go
done := make(chan bool)
// doSort is a lambda function, so a closure which knows the channel done:
doSort := func(s []int){
sort(s)
done <- true
}
i := pivot(s)
go doSort(s[:i])
go doSort(s[i:])
<-done
<-done
```
下边的代码用完整的信号灯模式对size长度的gloat64切片进行了N个`doSomething()`计算并同时完成通道sem分配了相同的长度切包含空接口类型的元素待所有的计算都完成后发送信号通过放入值。在循环中从通道sem不停的接收数据来等待所有的协程完成。
```go
type Empty interface {}
var empty Empty
...
data := make([]float64, N)
res := make([]float64, N)
sem := make(chan Empty, N)
...
for i, xi := range data {
go func (i int, xi float64) {
res[i] = doSomething(i, xi)
sem <- empty
} (i, xi)
}
// wait for goroutines to finish
for i := 0; i < N; i++ { <-sem }
```
注意闭合:`i``xi`都是作为参数传入闭合函数的,从外层循环中隐藏了变量`i``xi`。让每个协程有一份`i``xi`的拷贝另外for循环的下一次迭代会更新所有协程中`i``xi`的值。切片`res`没有传入闭合函数,因为协程不需要单独拷贝一份。切片`res`也在闭合函数中但并不是参数。
## 14.2.8 实现并行的for循环
在上一部分章节[14.2.7](14.2.7.md)的代码片段中for循环的每一个迭代是并行完成的
```go
for i, v := range data {
go func (i int, v float64) {
doSomething(i, v)
...
} (i, v)
}
```
在for循环中并行计算迭代可能带来很好的性能提升。不过所有的迭代都必须是独立完成的。有些语言比如Fortress或者其他并行框架以不同的结构实现了这种方式在Go中用协程实现起来非常容易
## 14.2.9 用带缓冲通道实现一个信号灯
## 链接