linux消息队列go的channel,Go channel功能详解-Go语言中文社区

在golang中,channel属于较为核心的一个功能,尤其在go协程中,channel功能尤为重要。作为goroutine之间通信的一种方式,channel跟Linux系统中的管道/消息队列有很多类似之处。使用channel可以方便地在goroutine之间传递数据,此外,channel还关联了数据类型,如int、string等等,可以决定确定channel中的数据单元。

[TOC]

定义channel

Channel类型的定义格式如下,包括三种类型的定义。可选的

ChannelType = ( "chan" | "chan" "

chan T // 可以接收和发送类型为 T 的数据

chan

//

chan

chan

chan (

和slice、map类似,可以使用make关键字来初始化channel。

unbuffered := make(chan int) //定义无缓冲的整型通道

buffered := make(chan string, 10) //有缓冲的字符串通道

上述代码中定义了一个无缓冲的channel和一个有缓冲channel。make的第一个参数需要是关键字chan,之后跟着允许通道交换的数据的类型。如果创建的是一个有缓冲channel,之后还需要在第二个参数指定这个channel的缓冲区的大小。和其他引用类型一样,channel 的空值为 nil ,使用 == 可以对类型相同的 channel 进行比较,只有指向相同对象或同为 nil 时,才返回 true。

无缓冲channel默认会阻塞读取操作,有缓冲channel能部分避免阻塞读取操作。据此特性可以实现很多应用场景,后文将会逐项介绍。

读写channel

buffered

value :=

Go使用操作符->实现channel的读写功能。需要注意的是,在执行读写操作之前必须先初始化此通道,否则会出现永久阻塞的现象。

关闭channel

使用go内置的close函数可以关闭channel,实际使用中经常使用 defer功能,在程序最后关闭channel。

close(buffered)

channel用处

gorouting通信

这一点勿需多言,前面介绍channel时就提到这一点,channel的下述特性均基于此项功能实现。

gorouting同步

对于unbuffered channel,缺省情况下发送和接收会一直阻塞着,直至另一方做好准备。用此特性可以实现gororutine之间的同步功能,而不必使用显式的锁或条件变量。

package main

import "fmt"

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

sum := 0

for _, v := range s {

sum += v

}

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 :=

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

}

上述代码执行结果如下

kefin@localhost:~/gopath/src/iotest $ go run sync.go

-5 17 12

上述代码是官方提供的例子,x, y :=

用于range遍历

package main

import (

"fmt"

"time"

)

func main() {

go func() {

time.Sleep(1 * time.Hour)

}()

c := make(chan int)

go func() {

for i := 0; i < 10; i = i + 1 {

c

}

close(c)

}()

for i := range c {

fmt.Println(i)

}

fmt.Println("Finished")

}

range c产生的迭代值为Channel中发送的值,它会一直迭代知道channel被关闭。上面的例子中如果把close(c)注释掉,程序会一直阻塞在for …… range那一行。代码的执行结果为

kefin@localhost:~/gopath/src/iotest $ go run range.go

0

1

2

3

4

5

6

7

8

9

Finished

配合select使用

select语句选择一组可能的send操作和receive操作去处理,它类似switch,但是只是用来处理通讯(communication)操作。 它的case可以是send语句,也可以是receive语句,亦或者default。receive语句可以将值赋值给一个或者两个变量,最多允许有一个default case,它可以放在case列表的任何位置,大部分会将它放在最后。

package main

import "fmt"

func fibonacci(c, quit chan int)

x, y := 0, 1

for {

select {

case c

x, y = y, x+y

case

fmt.Println("quit")

return

}

}

}

func main() {

c := make(chan int)

quit := make(chan int)

go func() {

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

fmt.Println(

}

quit

}()

fibonacci(c, quit)

}

如果有同时多个case去处理,比如同时有多个channel可以接收数据,那么Go会伪随机的选择一个case处理(pseudo-random)。如果没有case需要处理,则会选择default去处理。如果没有default case,则select语句会阻塞,直到某个case需要处理。需要注意的是,nil channel上的操作会一直被阻塞。如果没有default case,只有nil,那么channel的select会一直被阻塞。

此外,还可以配合select的超时处理功能,如上所述,没有case需要处理时,select语句就会一直阻塞,此时通常需要设置超时操作来处理超时的情况。 下面这个例子我们会在2秒后往channel c1中发送一个数据,但是select设置为1秒超时,因此我们会打印出timeout 1,而不是result 1。

package main

import (

"fmt"

"time"

)

func main() {

c1 := make(chan string, 1)

go func() {

time.Sleep(time.Second * 2)

c1

}()

select {

case res :=

fmt.Println(res)

case

fmt.Println("timeout 1")

}

}

其实利用的是time.After方法,它返回一个类型为

kefin@localhost:~/gopath/src/iotest $ go run select_timeout.go

timeout 1

实现Timer和Ticker

timer是一个定时器,代表未来的一个单一事件,可以设置timer需要等待多长时间,它提供一个Channel,在将来的那个时间那个Channel提供了一个时间值。下面的例子中第二行会阻塞2秒钟左右的时间,直到时间到了才会继续执行。当然如果只想单纯的等待2秒,可以使用time.Sleep(2)来实现。

timer1 := time.NewTimer(time.Second * 2)

fmt.Println("Timer 1 expired")

你还可以使用timer.Stop来停止[计时器]

timer2 := time.NewTimer(time.Second)

go func() {

fmt.Println("Timer 2 expired")

}()

stop2 := timer2.Stop()

if stop2 {

fmt.Println("Timer 2 stopped")

}

ticker是一个定时触发的计时器,它会以一个间隔(interval)往Channel发送一个事件(当前时间),而Channel的接收者可以以固定的时间间隔从Channel中读取事件。下面的例子中ticker每500毫秒触发一次,你可以观察输出的时间

ticker := time.NewTicker(time.Millisecond * 500)

go func() {

for t := range ticker.C {

fmt.Println("Tick at", t)

}

}()

同样,ticker也可以通过Stop方法来停止。一旦它停止,接收者不再会从channel中接收数据了。

channel操作注意事项

关闭一个未初始化(nil) 的 channel 或者重复关闭同一个channel均会产生 panic

向一个已关闭的 channel 中发送消息会产生 panic

从已关闭的 channel 读取消息不会产生 panic,且能读出 channel 中还未被读取的消息,若消息均已读出,则会读到类型的零值。

从已关闭的 channel 中读取消息永远不会阻塞,并且会返回 false ,据此可判断 channel 是否关闭

关闭 channel 会产生一个广播机制,所有向 channel 读取消息的 goroutine 都会收到消息

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值