分享
  1. 首页
  2. 文章

Go 之旅五: 并发

xugang · · 1967 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

本文是学习 A Tour of Go (中文参考 Go 之旅中文 ) 整理的笔记。介绍Go 语言线程,信道以及互斥锁的概念和使用方法。

1. Go 线程

$GOPATH/src/go_note/gotour/concurrency/goroutine/goroutine.go 源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* go 语言线程
*/
packagemain
import(
"fmt"
)
func say(sstring){
fori:=0;i<2;i++{
fmt.Println(i,s)
}
}
func main(){
go say("world")
say("hello")
}

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

1
2
gof(x,y,z)

会启动一个新的 Go 线程程并执行

1
2
f(x,y,z)

其中 fxyz 的求值在当前的 Go 线程中进行,而 f 的执行发生在新的 Go 线程中。

Go 程在相同的地址空间中运行,因此在访问共享的内存时必须进行同步,这可以通过使用sync 包或信道实现。

2. 信道

$GOPATH/src/go_note/gotour/concurrency/channel/channel.go 源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* go 语言信道
*/
packagemain
import"fmt"
func sum(s[]int,cchan int){
sum:=0
for_,v:=ranges{
sum+=v
}
c<-sum
}
func fibonacci(nint,cchan int){
x,y:=0,1
fori:=0;i<n;i++{
c<-x
x,y=y,x+y
}
close(c)// 关闭队列
}
func main(){
s:=[]int{7,2,8,-9,4,0}
c:=make(chan int)
go sum(s[:len(s)/2],c)
go sum(s[len(s)/2:],c)
x,y:=<-c,<-c// 从 c 中获取数据
fmt.Println(x,y,x+y)
// range 与 close
f:=make(chan int,10)// 创建带有缓冲区的管道
go fibonacci(cap(f),f)
fori:=rangef{
fmt.Println(i)
}
}

信道是带有类型的管道,通过信道操作符 <- 来从信道发送或者接收值。

1
2
3
ch&lt;-v// 将 v 发送至信道 ch。
v:=&lt;-ch// 从 ch 接收值并赋予 v。

"箭头"就是数据流的方向。

信道在使用前必须创建:

1
2
ch:=make(chan int)

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

2.1 带缓冲的信道

信道可以是带缓冲的, 将缓冲长度作为第二个参数提供给 make 来初始化一个带缓冲的信道:

1
2
ch:=make(chan int,100)

仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞。

2.2 range 和 close

发送者可通过 close 关闭一个信道来表示没有需要发送的值。接收者可以通过为接收表达式的第二个参数来测试信道是否被关闭:若没有值可以接收且信道已被关闭,该值为 false

1
2
3
v,ok:=&lt;-ch
// ok == false

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

注意: 只有发送者才能关闭信道,而接收者不能。向一个已经关闭的信道发送数据会引发程序错误。信道与文件不同,通常情况下无需关闭它们。只有在必须告诉接收者不再有值需要发送的时候才有必要关闭,如终止一个 range 循环。

3. select 语句

$GOPATH/src/go_note/gotour/concurrency/select/select.go 源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* go 语言 select
*/
packagemain
import(
"fmt"
)
// go 线程设置c 管道
func fibonacci(c,quit chan int){
x,y:=0,1
for{
select{
casec&lt;-x:
x,y=y,x+y
case&lt;-quit:
fmt.Println("quit")
return
default:
fmt.Println(" .")
}
}
}
func main(){
c:=make(chan int)
quit:=make(chan int)
// go 线程读取c 管道
go func(){
fori:=0;i&lt;5;i++{
fmt.Println(&lt;-c)
}
quit&lt;-0
}()
fibonacci(c,quit)
}

select 语句使一个 Go 线程可以等待多个通信操作。

select 会阻塞到某个分支可以继续执行为止,这时就会执行该分支。当多个分支都准备好时会随机选择一个执行。

select 中的其它分支都没有准备好时,default 分支就会执行。为了在尝试发送或者接收时不发生阻塞,可使用 default 分支

4. sync.Mutex

$GOPATH/src/go_note/gotour/concurrency/mutex/mutex.go 源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* go 互斥锁
*/
packagemain
import(
"fmt"
"sync"
"time"
)
// 并发安全
typeSafeCounterstruct{
vmap[string]int
mux sync.Mutex
}
// 增加计数器值
func(c *SafeCounter)Inc(key string){
c.mux.Lock()
c.v[key]++
c.mux.Unlock()
}
// 返回当前计数器值
func(c *SafeCounter)Value(key string)int{
c.mux.Lock()
deferc.mux.Unlock()// 在Value 函数返回时解锁
returnc.v[key]
}
func main(){
c:=SafeCounter{v:make(map[string]int)}
fori:=0;i&lt;1000;i++{
goc.Inc("somekey")
}
time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))//1000
}

互斥(mutual exclusion),指一次只有一个 Go 线程能够访问一个共享的变量。一般使用互斥锁(Mutex)来提供这种机制。

Go 标准库中提供了 sync.Mutex 互斥锁类型及其两个方法:

1
2
3
Lock()
Unlock()

通过在代码前调用 Lock() 方法,在代码后调用 Unlock() 方法来保证一段代码的互斥执行。另外可以用 defer 语句来保证互斥锁一定会被解锁。

参考

可以关注我的微博了解更多信息: @刚刚小码农


有疑问加站长微信联系(非本文作者)

本文来自:ironxu

感谢作者:xugang

查看原文:Go 之旅五: 并发

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1967 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏