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 都会收到消息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值