分享
  1. 首页
  2. 文章

Channels in Go

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

Go的Goroutine能够让我们并行的运行一些代码。但是要有效的利用它,需要一些额外的工作。当进程完成创建,我们应该能够数据传递正在运行进程,我们也应该能够获取数据正在运行进程,channels 做到了这一点并且能够很好的与goroutines工作。

我们可以把Channel想象成一个通道定义大小容量的传输带,一边可以往管道上面存放东西,另外一边可以将其取出

channels

Channels用来同步并发执行的函数并提供它们某种传值交流的机制。Channels的一些特性:通过channel传递的元素类型、容器(或缓冲区)和传递的方向由"<-"操作符指定。你可以使用内置函数 make分配一个channel:

1
2
3
4
5
i:=make(chan int)// by default the capacity is 0
s:=make(chan string,3)// non-zero capacity
r:=make(<-chan bool)// can only read from
w:=make(chan<-[]os.FileInfo)// can only write to

我们可以通过<- 操作符发送接收数据

1
2
3
4
5
6
7
8
my_channel:=make(chan int)
//within some goroutine - to put a value on the channel
my_channel<-5
//within some other goroutine - to take a value off the channel
varmy_recvd_value int
my_recvd_value=<-my_channel
1
2
ic_send_only:=make(<-chan int)//a channel that can only send data - arrow going out is sending
ic_recv_only:=make(chan<-int)//a channel that can only receive a data - arrow going in is receiving

查看一个完整的示例:

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
packagemain
import(
"fmt"
"time"
"strconv"
)
variint
func makeCakeAndSend(cs chan string){
i=i+1
cakeName:="Strawberry Cake "+strconv.Itoa(i)
fmt.Println("Making a cake and sending ...",cakeName)
cs<-cakeName//send a strawberry cake
}
func receiveCakeAndPack(cs chan string){
s:=<-cs//get whatever cake is on the channel
fmt.Println("Packing received cake: ",s)
}
func main(){
cs:=make(chan string)
fori:=0;i<3;i++{
go makeCakeAndSend(cs)
go receiveCakeAndPack(cs)
//sleep for a while so that the program doesn’t exit immediately and output is clear for illustration
time.Sleep(1*1e9)
}
}

输出的结果:

1
2
3
4
5
6
Makingacake andsending...Strawberry Cake1
Packing received cake:Strawberry Cake1
Makingacake andsending...Strawberry Cake2
Packing received cake:Strawberry Cake2
Makingacake andsending...Strawberry Cake3
Packing received cake:Strawberry Cake3

其实并不建议将goroutine放在 for 循环中

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
packagemain
import(
"fmt"
"time"
"strconv"
)
func makeCakeAndSend(cs chan string){
fori:=1;i<=3;i++{
cakeName:="Strawberry Cake "+strconv.Itoa(i)
fmt.Println("Making a cake and sending ...",cakeName)
cs<-cakeName//send a strawberry cake
}
}
func receiveCakeAndPack(cs chan string){
fori:=1;i<=3;i++{
s:=<-cs//get whatever cake is on the channel
fmt.Println("Packing received cake: ",s)
}
}
func main(){
cs:=make(chan string)
go makeCakeAndSend(cs)
go receiveCakeAndPack(cs)
//sleep for a while so that the program doesn’t exit immediately
time.Sleep(4*1e9)
}

对比上面的输出结果:

1
2
3
4
5
6
Makingacake andsending...Strawberry Cake1
Makingacake andsending...Strawberry Cake2
Packing received cake:Strawberry Cake1
Packing received cake:Strawberry Cake2
Makingacake andsending...Strawberry Cake3
Packing received cake:Strawberry Cake3

缓冲

为了避免存在一个channel的缓冲区所有读取操作都在没有锁定的情况下顺利完成(如果缓冲区是空的)并且写入操作也顺利结束(缓冲区不满),这样的channel被称作非同步的channel。下面是一个用来描述这两者区别的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func main(){
message:=make(chan string)
count:=3
go func(){
fori:=1;i<=count;i++{
fmt.Println("send message")
message<-fmt.Sprintf("message %d",i)
}
}()
time.Sleep(time.Second *3)
fori:=1;i<=count;i++{
fmt.Println(<-message)
}
}
1
2
3
4
5
6
7
send message
// waite 3 second
message1
send message
send message
message2
message3

现在我们提供一个缓冲区给输出信息的channel,例如:定义初始化行将被改为:message := make(chan string, 2)。这次程序输出将变为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func main(){
message:=make(chan string,2)
count:=3
go func(){
fori:=1;i<=count;i++{
fmt.Println("send message")
message<-fmt.Sprintf("message %d",i)
}
}()
time.Sleep(time.Second *3)
fori:=1;i<=count;i++{
fmt.Println(<-message)
}
}
1
2
3
4
5
6
7
send message
send message
send message
// wait 3 second
message1
message2
message3

这里我们看到所有的写操作的执行都不会等待第一次对缓冲区的读取操作结束,channel允许储存所有的三条信息。通过修改channel容器,我们通过可以控制处理信息的总数达到限制系统输出的目的。

死锁

1
2
3
4
5
6
func main(){
c:=make(chan int)
c<-42// write channel
val:=<-c// read channel
println(val)
}

我们会看到编译器报错:

1
2
3
fatal error:all goroutines are asleep-deadlock!
goroutine1[chan send]:

这个错误就是我们所知的死锁. 在这种情况下,两个goroutine互相等待对方释放资源,造成双方都无法继续运行。GO语言可以在运行时检测这种死锁并报错。这个错误是因为锁的自身特性产生的。

代码在次以单线程的方式运行,逐行运行。向channel写入的操作(c <- 42)会锁住整个程序的执行进程,因为在同步channel中的写操作只有在读取器准备就绪后才能成功执行。然而在这里,我们在写操作的下一行才创建了读取器。

为了使程序顺利执行,我们需要做如下改动:

1
2
3
4
5
6
7
8
func main(){
c:=make(chan int)
go func(){
c<-42
}()
val:=<-c
println(val)
}

运行上面代码 就可以看到正常输出42了。

参考文献:http://golangtutorials.blogspot.hk/2011/06/channels-in-go.html

Channels in Go

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

本文来自:谢权SELF

感谢作者:谢权

查看原文:Channels in Go

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

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

用户登录

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

今日阅读排行

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

一周阅读排行

    加载中

关注我

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

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

给该专栏投稿 写篇新文章

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

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