mirror of
https://github.com/unknwon/the-way-to-go_ZH_CN.git
synced 2025-08-11 23:52:31 +08:00
完成14.2
This commit is contained in:
168
eBook/14.2.md
168
eBook/14.2.md
@@ -456,7 +456,175 @@ func (c *container) Iter () <- chan items {
|
||||
return ch
|
||||
}
|
||||
```
|
||||
在协程里,一个for循环迭代容器c中的元素(对于树或图的算法,这种简单的for循环可以替换为深度优先搜索)。
|
||||
|
||||
调用这个方法的代码可以这样迭代容器:
|
||||
```go
|
||||
for x := range container.Iter() { ... }
|
||||
```
|
||||
可以运行在自己的协程中,所以上边的迭代用到了一个通道和两个协程(可能运行在两个线程上)。就有了一个特殊的生产者-消费者模式。如果程序在协程给通道写完值之前结束,协程不会被回收;设计如此。这种行为看起来是错误的,但是通道是一种线程安全的通信。在这种情况下,协程尝试写入一个通道,而这个通道永远不会被读取,这可能是个bug而并非期望它被静默的回收。
|
||||
|
||||
习惯用法:生产者消费者模式
|
||||
|
||||
假设你有`Produce()`函数来产生`Consume`函数需要的值。它们都可以运行在独立的协程中,生产者在通道中放入给消费者读取的值。整个处理过程可以替换为无限循环:
|
||||
```go
|
||||
for {
|
||||
Consume(Produce())
|
||||
}
|
||||
```
|
||||
|
||||
## 14.2.11 通道的方向
|
||||
|
||||
通道类型可以用注解来表示它只发送或者只接收:
|
||||
```go
|
||||
var send_only chan<- int // channel can only receive data
|
||||
var recv_only <-chan int // channel can onley send data
|
||||
```
|
||||
只接收的通道(<-chan T)无法关闭,因为关闭通道是发送者用来表示不再给通道发送值了,所以对只接收通道是没有意义的。通道创建的时候都是双向的,但也可以分配有方向的通道变量,就像以下代码:
|
||||
```go
|
||||
var c = make(chan int) // bidirectional
|
||||
go source(c)
|
||||
go sink(c)
|
||||
|
||||
func source(ch chan<- int){
|
||||
for { ch <- 1 }
|
||||
}
|
||||
|
||||
func sink(ch <-chan int) {
|
||||
for { <-ch }
|
||||
}
|
||||
```
|
||||
|
||||
习惯用法:管道和选择器模式
|
||||
|
||||
更具体的例子还有协程处理它从通道接收的数据并发送给输出通道:
|
||||
```go
|
||||
sendChan := make(chan int)
|
||||
reciveChan := make(chan string)
|
||||
go processChannel(sendChan, receiveChan)
|
||||
|
||||
func processChannel(in <-chan int, out chan<- string) {
|
||||
for inValue := range in {
|
||||
result := ... /// processing inValue
|
||||
out <- result
|
||||
}
|
||||
}
|
||||
```
|
||||
通过使用方向注解来限制协程对通道的操作。
|
||||
|
||||
这里有一个来自Go指导的很赞的例子,打印了输出的主要数字,使用选择器(‘筛’)作为它的算法。每个素数都有一个选择器,如下图:
|
||||
|
||||

|
||||
|
||||
版本1: 示例 14.7-[sieve1.go](examples/chapter_14/sieve1.go)
|
||||
```go
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.package main
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Send the sequence 2, 3, 4, ... to channel 'ch'.
|
||||
func generate(ch chan int) {
|
||||
for i := 2; ; i++ {
|
||||
ch <- i // Send 'i' to channel 'ch'.
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the values from channel 'in' to channel 'out',
|
||||
// removing those divisible by 'prime'.
|
||||
func filter(in, out chan int, prime int) {
|
||||
for {
|
||||
i := <-in // Receive value of new variable 'i' from 'in'.
|
||||
if i%prime != 0 {
|
||||
out <- i // Send 'i' to channel 'out'.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The prime sieve: Daisy-chain filter processes together.
|
||||
func main() {
|
||||
ch := make(chan int) // Create a new channel.
|
||||
go generate(ch) // Start generate() as a goroutine.
|
||||
for {
|
||||
prime := <-ch
|
||||
fmt.Print(prime, " ")
|
||||
ch1 := make(chan int)
|
||||
go filter(ch, ch1, prime)
|
||||
ch = ch1
|
||||
}
|
||||
}
|
||||
```
|
||||
协程`filter(in, out chan int, prime int)`拷贝整数到输出通道,丢弃掉可以被prime整除的数字。然后每个prime又开启了一个新的协程,生成器和选择器并发请求。
|
||||
```
|
||||
输出:2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101
|
||||
103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223
|
||||
227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349
|
||||
353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479
|
||||
487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619
|
||||
631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769
|
||||
773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929
|
||||
937 941 947 953 967 971 977 983 991 997 1009 1013...
|
||||
```
|
||||
第二个版本引入了上边的习惯用法:函数`sieve`,`generate`,和`filter`都是工厂;它们创建通道并返回,而且使用了协程的lambda函数。`main`函数现在短小清晰:它调用`sieve()`返回了包含素数的通道,然后通过`fmt.Println(<-primes)`打印出来。
|
||||
|
||||
版本2:示例 14.8-[sieve2.go](examples/chapter_14/sieve2.go)
|
||||
```go
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Send the sequence 2, 3, 4, ... to returned channel
|
||||
func generate() chan int {
|
||||
ch := make(chan int)
|
||||
go func() {
|
||||
for i := 2; ; i++ {
|
||||
ch <- i
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
// Filter out input values divisible by 'prime', send rest to returned channel
|
||||
func filter(in chan int, prime int) chan int {
|
||||
out := make(chan int)
|
||||
go func() {
|
||||
for {
|
||||
if i := <-in; i%prime != 0 {
|
||||
out <- i
|
||||
}
|
||||
}
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
func sieve() chan int {
|
||||
out := make(chan int)
|
||||
go func() {
|
||||
ch := generate()
|
||||
for {
|
||||
prime := <-ch
|
||||
ch = filter(ch, prime)
|
||||
out <- prime
|
||||
}
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
func main() {
|
||||
primes := sieve()
|
||||
for {
|
||||
fmt.Println(<-primes)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 链接
|
||||
|
Reference in New Issue
Block a user