在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
使用协程这种并发模式是趋势,协程的基本要求是:并发执行和可大量创建。 一些语言已经支持协程,下面这个图来自:http://qing.weibo.com/tj/88ca09aa33002ele.html 这种并发模式的内核只需要协程和通道就够了。其中协程负责执行代码,通道负责在协程之间传递事件。
协程是轻量级的线程。在过程式编程中,当调用一个过程的时候,需要等待其执行完才返回。而调用一个协程的时候,不需要等待其执行完,会立即返回。协程十分轻量,Go语言可以在一个进程中执行有数以十万计的协程,依旧保持高性能。而对于普通的平台,一个进程有数千个线程,其CPU会忙于上下文切换,性能急剧下降。随意创建线程可不是一个好主意,但是我们可以大量使用的协程。 通道是协程之间的数据传输通道。通道可以在众多的协程之间传递数据,数据具体可以值也可以是个引用。通道有两种使用方式。
如此,通道就可以在传递数据的同时,控制协程的运行。有点像事件驱动,也有点像阻塞队列。
Go语言创建协程很简单,只需要简单的在函数前用关键字 go即可。 https://github.com/astaxie/build-web-application-with-golang/blob/master/ebook/02.7.md 下面的代码例子是通过2分法,开两个协程分别计算和,然后再合并这两个计算和。 注意,这里的 通道 写入和读取都是阻塞的, 这样就可以保证 两个汇总都计算完了,才会执行 fmt.Println(x, y, x+y) package main
import "fmt"
func sum(a []int, c chan int) { sum := 0 for _, v := range a { sum += v } c <- sum // send sum to c }
func main() { a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int) go sum(a[:len(a)/2], c) go sum(a[len(a)/2:], c) x, y :=<-c, <-c // receive from c
fmt.Println(x, y, x+y) } 默认情况下,channel接收和发送数据都是阻塞的,除非另一端已经准备好,这样就使得Goroutines同步变的更加的简单,而不需要显式的lock。 所谓阻塞,也就是如果读取不到 (value := <-ch)它将会被阻塞,直到有数据接收。 另外,如果ch中有数据,任何发送(ch<-5)将会被阻塞,直到数据被读出。无缓冲channel是在多个goroutine之间同步很棒的工具。
Buffered ChannelsGo也允许指定channel的缓冲大小,很简单,就是channel可以存储多少元素。
比如下面的代码会报错误: package main
import "fmt"
func main() { c :=make(chanint, 1) //1报错,修改2为3可以正常运行 c <- 1 c <- 2 fmt.Println(<-c) fmt.Println(<-c) } 错误信息: throw: all goroutines are asleep - deadlock!
goroutine 1 [chan send]: main.main() /Users/cybercare/go/src/test1/main.go:8 +0x70
goroutine 2 [syscall]: created by runtime.main /usr/local/go/src/pkg/runtime/proc.c:221 Range和CloseChannel 也可以用 Range 进行遍历。 下面的例子是利用协程计算 斐波那契數列,每次计算出来的值都通过通道打印出来。直到调用close关闭通道。 package main
import ( "fmt" )
func fibonacci(n int, c chan int) { x, y := 1, 1 for i := 0; i < n; i++ { c <- x x, y = y, x+y } close(c) }
func main() { c := make(chan int, 10) go fibonacci(cap(c), c) for i := range c { fmt.Println(i) } } 这段代码执行的结果如下:除了前2个数,其他数都是前两个数相加之和。 1 生产者通过关键字 记住应该在生产者的地方关闭channel,而不是消费的地方去关闭它 Select上面介绍的都是只有一个channel的情况,那么如果存在多个channel的时候,我们可以通过
在select里面还有default语法,select其实就是类似switch的功能,default就是当监听的channel都没有准备好的时候,默认执行的(select不再阻塞等待channel)。 http://www.sharejs.com/codes/go/4415 下面代码是斐波那契數列的一个调整,执行结果不确定,这是因为select是随机的选择一个执行的。这里有default函数,default函数也会随机被执行到。如果没有default函数,这个执行结果是固定的。 package main
import "fmt"
func fibonacci(c, quit chan int) { x, y := 1, 1 for { select { case c <- x: x, y = y, x+y case <-quit: fmt.Println("quit") return default: fmt.Println("default") } } }
func main() { c := make(chan int) quit := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 }() fibonacci(c, quit) }
超时select 语句使得一个 goroutine 在多个通讯操作上等待。 对 select 的 case ,它只能是 receive, send , assign recv 三者之一。 http://golang.org/pkg/time/#After https://code.google.com/p/go-wiki/wiki/Timeouts
package main
import ( "fmt" "time" )
func main() { c := make(chan int) o := make(chan bool) go func() { for { select { case v := <-c: fmt.Println(v) case <-time.After(5 * time.Second): fmt.Println("timeout 5s") o <- true break } } }() <-o }
这里信道 o 的目的就是确保 协程 一直被执行。 我们如果从信道 c 中读取数据超时 5秒的话,就会触发 <-time.After(5 * time.Second), 继而给信道 o 中放入一个数据,从而应用关闭。
真实的超时代码应该是类似下面方式的伪代码: import "time"
Go语言_并发篇 2.7 并发 Go-简洁的并发 Go语言并发之美 Go的并发模式:超时、继续 go 语言并发机制 goroutine 初探 Go语言并发 |
请发表评论