12.通道简介

通道通过收发消息,使得能够以推送方式协调并发事件,事件发生时,可将触发的消息推送给接收者。使用共享内存时,程序必须检查共享内存。在变化频繁的并发编程中,很多人都认为使用消息是一种更佳的通信方式

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
喜欢就支持以下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容