From c1466e58c92811eeb960d4bfb6894c426aae77c0 Mon Sep 17 00:00:00 2001 From: glight2000 <173959153@qq.com> Date: Mon, 28 Dec 2015 23:36:19 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=9014.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eBook/14.2.md | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/eBook/14.2.md b/eBook/14.2.md index 7001551..62a3507 100644 --- a/eBook/14.2.md +++ b/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) + } +} +``` ## 链接