glight2000 10 лет назад
Родитель
Сommit
5095fe05c3
1 измененных файлов с 103 добавлено и 1 удалено
  1. 103 1
      eBook/14.2.md

+ 103 - 1
eBook/14.2.md

@@ -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 用带缓冲通道实现一个信号灯
 
 ## 链接