10.笔记go语言——并发

来源:互联网 发布:javlibrary新域名 12 编辑:程序博客网 时间:2024/06/01 22:57

10.笔记go语言——并发

goroutine

goroutine 是由 Go 运行时环境管理的轻量级线程。

go f(x, y, z)

开启一个新的 goroutine 执行

f(x, y, z)

f , x , y 和 z 是当前 goroutine中定义的,但是在新的 goroutine 中运行 `f`。

goroutine 在相同的地址空间中运行,因此访问共享内存必须进行同步。sync 提供了这种可能,不过在Go 中并不经常用到,因为有其他的办法。

package main

import (

           "fmt"

           "time"

)

func say(s string) {

           for i:= 0; i < 5; i++ {

                     time.Sleep(100* time.Millisecond)

                     fmt.Println(s)

           }

}

func main() {

           gosay("world")

           say("hello")

}

channel

channel 是有类型的管道,可以用 channel 操作符 <- 对其发送或者接收值。

ch <- v    // 将 v 送入 channel ch。

v := <-ch  // 从 ch 接收,并且赋值给 v。

(“箭头”就是数据流的方向。)

和 map 与 slice 一样,channel 使用前必须创建:

ch := make(chan int)

默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得 goroutine 可以在没有明确的锁或竞态变量的情况下进行同步。

package main

import "fmt"

 

func sum(a []int, c chan int) {

           sum:= 0

           for_, v := range a {

                     sum+= v

           }

           c<- sum // 将和送入c

}

 

func main() {

           a :=[]int{7, 2, 8, -9, 4, 0}

 

           c :=make(chan int)

           gosum(a[:len(a)/2], c)

           gosum(a[len(a)/2:], c)

           x, y:= <-c, <-c // c中获取

 

           fmt.Println(x,y, x+y)

}

执行:

           -517 12

缓冲 channel

channel 可以是 _带缓冲的_。为 make 提供第二个参数作为缓冲长度来初始化一个缓冲 channel:

ch := make(chan int, 100)

向缓冲 channel 发送数据的时候,只有在缓冲区满的时候才会阻塞。当缓冲区清空的时候接受阻塞。

修改例子使得缓冲区被填满,然后看看会发生什么。

package main

import "fmt"

func main() {

           c :=make(chan int, 2)

           c<- 1

           c<- 2

           fmt.Println(<-c)

           fmt.Println(<-c)

}

执行:

           1

           2

range 和 close

发送者可以 close 一个 channel 来表示再没有值会被发送了。接收者可以通过赋值语句的第二参数来测试 channel 是否被关闭:当没有值可以接收并且 channel 已经被关闭,那么经过v, ok := <-ch

之后 ok 会被设置为 `false`。

循环 `for i := range c` 会不断从 channel 接收值,直到它被关闭。

注意: 只有发送者才能关闭 channel,而不是接收者。向一个已经关闭的 channel 发送数据会引起 panic。 还要注意: channel 与文件不同;通常情况下无需关闭它们。只有在需要告诉接收者没有更多的数据的时候才有必要进行关闭,例如中断一个 `range`。

package main

import (

           "fmt"

)

func fibonacci(n int, c chan int) {

           x, y:= 0, 1

           for i:= 0; i < n; i++ {

                     c<- x

                     x,y = y, x+y

           }

           close(c)

}

 

func main() {

           c :=make(chan int, 10)

           gofibonacci(cap(c), c)

           for i:= range c {

                     fmt.Println(i)

           }

}

执行:

0

           1

           1

           2

           3

           5

           8

           13

           21

           34

select

select 语句使得一个 goroutine 在多个通讯操作上等待。

select 会阻塞,直到条件分支中的某个可以继续执行,这时就会执行那个条件分支。当多个都准备好的时候,会随机选择一个。

package main

import "fmt"

func fibonacci(c, quit chan int) {

           x, y:= 0, 1

           for {

                     select{

                     casec <- x:

                                x,y = y, x+y

                     case<-quit:

                                fmt.Println("quit")

                                return

                     }

           }

}

func main() {

           c :=make(chan int)

           quit:= make(chan int)

           gofunc() {

                     fori := 0; i < 10; i++ {

                                fmt.Println(<-c)

                     }

                     quit<- 0

           }()

           fibonacci(c,quit)

}

执行:

           0

           1

           1

           2

           3

           5

           8

           13

           21

           34

           quit

默认选择

当 select 中的其他条件分支都没有准备好的时候,`default` 分支会被执行。

为了非阻塞的发送或者接收,可使用 default 分支:

select {

case i := <-c:

   // 使用 i

default:

   // 从 c 读取会阻塞

}

package main

import (

           "fmt"

           "time"

)

func main() {

           tick:= time.Tick(100 * time.Millisecond)

           boom:= time.After(500 * time.Millisecond)

           for {

                     select{

                     case<-tick:

                                fmt.Println("tick.")

                     case<-boom:

                                fmt.Println("BOOM!")

                                return

                     default:

                                fmt.Println("    .")

                                time.Sleep(50* time.Millisecond)

                     }

           }

}

执行:

.

               .

           tick.

               .

               .

           tick.

               .

               .

           tick.

               .

               .

           tick.

               .

               .

           BOOM!

入门结束

Go 文档 是一个极好的 开始。 它包含了参考、指南、视频等等更多资料。

了解如何组织 Go 代码并在其上工作,参阅 这个视频,或者阅读 如何编写 Go代码

在标准库上需要帮助的话,参考 包手册。语言本身的帮助,阅读 语言规范是件令人愉快的事情。

进一步探索 Go 的并发模型,参阅 Go 并发模型 (幻灯片) 以及 深入 Go并发模型 (幻灯片) 并且阅读 使用通讯共享内存 的代码之旅。

想要开始编写 Web 应用,参阅 一个简单的编程环境 (幻灯片) 并且阅读 编写 Web应用 的指南.

GO 中的一等公民函数 展示了有趣的函数类型。

Go Blog 有着众多的关于 Go 的文章信息。

mikespook 的博客有大量中文的关于 Go 的文章和翻译。

开源电子书 Go Web 编程 和 Go入门指南 能够帮助你更加深入的了解和学习 Go 语言。

访问 golang.org 了解更多内容。

关于本项目(中文)的任何意见、建议,请在这里提交 Issues。