通道通过收发消息,使得能够以推送方式协调并发事件,事件发生时,可将触发的消息推送给接收者。使用共享内存时,程序必须检查共享内存。在变化频繁的并发编程中,很多人都认为使用消息是一种更佳的通信方式
package main
import (
"fmt"
"time"
)
func slowFunc(){
fmt.Println("begin")
time.Sleep(time.Second * 2)
fmt.Println("sleeper() finished")
}
func main(){
go slowFunc()
fmt.Println("succeed")
//阻止程序退出
time.Sleep(time.Second * 3)
}
// succeed
// begin
// sleeper() finished
在上面的例子中,我们使用定时器的方式来阻止Goroutine返回,如果能够在Goroutine和程序之间通信,并让Goroutine结束时能够告诉主程序就好了
//创建通道
c := make(chan string)
- 使用内置函数Make创建一个通道,这是使用关键字chan指定的
- 关键字chan后面的string指出这个通道将用于存储字符串的数据,这意味着这个通道只能用于收发字符串值
//向通道发送消息
c <- "Hello World"
//从通道中接受消息
mag := <- c
package main
import (
"fmt"
"time"
)
var c = make(chan string)
func slow(){
time.Sleep(time.Second * 2)
fmt.Println("slow finished")
c <- "finished"
}
func main(){
go slow()
msg := <- c
fmt.Println(msg)
}
// slow finished
// finished
使用缓冲通道
通常通道收到消息后可将其发送给接收者,但有时候可能没有接收者,在这种情况下,可使用缓冲通道。缓冲意味着可将数据存储在通道中,等接收者准备就绪再交给它。要创建缓冲通道,可向内置函数make传递另一个表示缓冲区长度的参数
//代表这个通道最多只能够存储两条信息
messages := make(chan string,2)
package main
import (
"fmt"
"time"
)
func slowFunc(c chan string){
for msg := range c{
fmt.Println(msg)
}
}
func main(){
message := make(chan string,2)
message <- "hello"
message <- "world"
close(message)
fmt.Println("Push two messages onto channe1")
time.Sleep(time.Second * 2)
slowFunc(message)
}
// Push two messages onto channe1
// hello
// world
- 创建一个长度为2的缓冲通道
- 向通道发送两条消息,此时没有可用的接收者,因此消息被缓冲
- 关闭通道,这意味着不能再向它发送消息
- 程序打印一条消息,指出通道包含两条消息,再休眠1s
- 将通道作为参数传递给函数
- 函数使用range遍历通道
阻塞和流程控制
package main
import (
"fmt"
"time"
)
func slowFunc(c chan string){
t := time.NewTicker(1 *time.Second)
for {
c <- "ping"
<- t.C
}
}
func main(){
messages := make(chan string)
go slowFunc(messages)
for {
msg := <-messages
fmt.Println(msg)
}
}
//ping
//ping
//...
将通道用作函数参数
要进一步指定在函数中如何使用传入的通道,可在传递通道时将其指定为只读、只写或读写
//进行只读传入
func channelReader(messages <- chan string){
msg := <- messages
fmt.Println(msg)
}
//只写传入
func channaleWriter(messages chan <- string){
messages <- "Hello world"
}
//读写传入
func channelReaderAndWriter(messages chan string){
msg := <- messages
fmt.Println(msg)
messages <- "hello world"
}
通过指定通道访问权限,有助于确保通道中数据的完整性,还可指定程序的哪部分可向通道发送数据或接收来自通道的数据
使用select语句
select语句与switch语句很相似,它为通道创建一系列接收者,并执行最先收到消息的接收者
channel1 := make(chan string)
channel2 := make(chan string)
select {
case msg1 := <-channel1:
fmt.Println("received1")
case msg2 := <-channel2:
fmt.Println("received2")
}
具体执行哪条case语句,取决于消息到达的时间,哪条消息最先到达决定了将执行哪条case语句。通常,接下来收到的其他消息将会被丢弃
package main
import (
"fmt"
"time"
)
func channel1(c chan string){
time.Sleep(time.Second * 2)
c <- "channel1"
}
func channel2(c chan string){
time.Sleep(time.Second * 1)
c <- "channel2"
}
func main(){
a := make(chan string)
b := make(chan string)
go channel2(a)
go channel1(b)
select{
case msg1 := <- a:
fmt.Println(msg1)
case msg2 := <- b:
fmt.Println(msg2)
}
}
// channel2
以上程序能够根据最先到达的消息采取相应的措施,但是如果没有接收到消息呢?因此我们可以使用一个超时时间,让select语句在指定时间后不再阻塞
select{
case msg1 := <- a:
fmt.Println(msg1)
case msg2 := <- b:
fmt.Println(msg2)
//在0.5秒内没有收到消息
case <- time.After(500 * time.Milisecond):
fmt.Println("no messages received.giving up")
}
推出通道
在已知停止执行的时间的情况下,使用超时时间是不错的选择,但在有些情况下,不确定select语句该在何时返回,因此不能使用定时器。在这种情况下,可使用退出通道
package main
import(
"fmt"
"time"
)
func sender(c chan string){
t := time.NewTicker (1 * time.Second)
for {
c <- "i am sending a message"
<- t.C
}
}
func main(){
c := make(chan string)
stop := make(chan string)
go sender(c)
go func(){
time.Sleep(time.Second * 3)
stop <- "stop"
}()
for {
select{
case msg := <- c:
fmt.Println(msg)
case <-stop:
return
}
}
}
// i am sending a message
// i am sending a message
// i am sending a message
以上这个程序,将会在运行3秒钟之后停止
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容