Update 14.2.md

This commit is contained in:
glight2000
2015-12-26 17:19:39 +08:00
parent 5095fe05c3
commit 7f82cbd0cd

View File

@@ -230,9 +230,9 @@ sum := <- ch // wait for, and retrieve the sum
在其他协程运行时让main程序无限阻塞的通常做法是在`main`函数的最后放置一个{}。
也可以使用通道让`main`程序等待协程完成,就是所谓的信号模式,我们会在接下来的部分讨论。
也可以使用通道让`main`程序等待协程完成,就是所谓的信号模式,我们会在接下来的部分讨论。
## 14.2.7 信号模式
## 14.2.7 信号模式
下边的片段阐明:协程通过在通道`ch`中放置一个值来处理结束的信号。`main`协程等待`<-ch`直到从中获取到值。
@@ -274,7 +274,7 @@ go doSort(s[i:])
<-done
<-done
```
下边的代码,用完整的信号模式对size长度的gloat64切片进行了N个`doSomething()`计算并同时完成通道sem分配了相同的长度切包含空接口类型的元素待所有的计算都完成后发送信号通过放入值。在循环中从通道sem不停的接收数据来等待所有的协程完成。
下边的代码,用完整的信号模式对size长度的gloat64切片进行了N个`doSomething()`计算并同时完成通道sem分配了相同的长度切包含空接口类型的元素待所有的计算都完成后发送信号通过放入值。在循环中从通道sem不停的接收数据来等待所有的协程完成。
```go
type Empty interface {}
var empty Empty
@@ -307,7 +307,100 @@ for i, v := range data {
```
在for循环中并行计算迭代可能带来很好的性能提升。不过所有的迭代都必须是独立完成的。有些语言比如Fortress或者其他并行框架以不同的结构实现了这种方式在Go中用协程实现起来非常容易
## 14.2.9 用带缓冲通道实现一个信号
## 14.2.9 用带缓冲通道实现一个信号
信号量是实现互斥锁(排外锁)常见的同步机制,限制对资源的访问,解决读写问题,比如没有实现信号量的`sync`的Go包使用带缓冲的通道可以轻松实现
* 带缓冲通道的容量和我们要同步的资源容量相同
* 通道的长度(当前存放的元素个数)当前资源被使用的数量相同
* 容量减去通道的长度就是未处理的资源个数(标准信号量的整数值)
不用管通道中存放的是什么只关注长度因此我们创建了一个有长度变量为0字节的通道
```go
type Empty interface {}
type semaphore chan Empty
```
将可用资源的数量N来初始化信号量`semaphore` `sem = make(semaphore, N)`
然后直接对信号量进行操作:
```go
// acquire n resources
func (s semaphore) P(n int) {
e := new(Empty)
for i := 0; i < n; i++ {
s <- e
}
}
// release n resouces
func (s semaphore) V(n int) {
for i:= 0; i < n; i++{
<- s
}
}
```
可以用来实现一个互斥的例子:
```go
/* mutexes */
func (s semaphore) Lock() {
s.P(1)
}
func (s semaphore) Unlock(){
s.V(1)
}
/* signal-wait */
func (s semaphore) Wait(n int) {
s.P(n)
}
func (s semaphore) Signal() {
s.V(1)
}
```
练习 14.5[gosum.go](exercises/chapter_14/gosum.go)用这种习惯用法写一个程序开启一个协程来计算2个整数的合并等待计算结果并打印出来。
练习 14.6[producer_consumer.go](exercises/chapter_14/producer_consumer.go)用这种习惯用法写一个程序有两个协程第一个提供数字01020...90并将他们放入通道,第二个协程从通道中读取并打印。`main()`等待两个协程完成后再结束。
习惯用法:通道工厂模式
编程中常见另外一种模式如下不将通道作为参数传递给协程而用函数来生成一个通道并返回工厂角色函数内有个lambda函数被协程调用。
在[channel_block2.go](examples/chapter_14/channel_block2.go)加入这种模式便有了示例 14.5-[channel_idiom.go](examples/chapter_14/channel_idiom.go)
```go
package main
import (
"fmt"
"time"
)
func main() {
stream := pump()
go suck(stream)
time.Sleep(1e9)
}
func pump() chan int {
ch := make(chan int)
go func() {
for i := 0; ; i++ {
ch <- i
}
}()
return ch
}
func suck(ch chan int) {
for {
fmt.Println(<-ch)
}
}
```
## 14.2.10 给通道使用For循环
## 链接