glight2000 10 yıl önce
ebeveyn
işleme
c1466e58c9
1 değiştirilmiş dosya ile 168 ekleme ve 0 silme
  1. 168 0
      eBook/14.2.md

+ 168 - 0
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指导的很赞的例子,打印了输出的主要数字,使用选择器(‘筛’)作为它的算法。每个素数都有一个选择器,如下图:
+
+![](../images/14.2_fig14.2.png?raw=true)
+
+版本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)
+	}
+}
+```
 
 
 ## 链接