GO:channel

概念

channel是goroutine之间的通信机制。每个channel都有一个数据类型,指定发送该类型的数据;同时同一时刻只有一个goroutine可以访问channel进行数据发送或访问;channel遵循FIFO原则。
channel的数据传输有以下特点:

  1. channel的发送方和接收方是不同的goroutine(因为数据的发送和接收行为都会阻塞);
  2. channel一次只能接收一个元素。
    参考

demo

向channel发送一个数据并接收

package main
import (
    "fmt"
)
func main() {
    // 构建一个通道
    ch := make(chan int)
    // 开启一个并发匿名函数
    go func() {
        fmt.Println("start goroutine")
        // 通过通道通知main的goroutine
        ch <- 0
        fmt.Println("exit goroutine")
    }()
    fmt.Println("wait goroutine")
    // 等待匿名goroutine
    <-ch
    fmt.Println("all done")
}

执行代码,输出如下:

wait goroutine
start goroutine
exit goroutine
all done

代码说明如下:

第 10 行,构建一个同步用的通道。
第 13 行,开启一个匿名函数的并发。
第 18 行,匿名 goroutine 即将结束时,通过通道通知 main 的 goroutine,这一句会一直阻塞直到 main 的 goroutine 接收为止。
第 27 行,开启 goroutine 后,马上通过通道等待匿名 goroutine 结束。

向channel连续发送数据并接收

package main

import (
    "fmt"

    "time"
)

func main() {

    // 构建一个通道
    ch := make(chan int)

    // 开启一个并发匿名函数
    go func() {

        // 从3循环到0
        for i := 3; i >= 0; i-- {

            // 发送3到0之间的数值
            ch <- i
		
            // 每次发送完时等待
            time.Sleep(time.Second)
        }

    }()

    // 遍历接收通道数据
    for data := range ch {

        // 打印通道数据
        fmt.Println(data)

        // 当遇到数据0时, 退出接收循环
        if data == 0 {
                break
        }
    }

}

执行代码,输出如下:

3
2
1
0

代码说明如下:
第 12 行,通过 make 生成一个整型元素的通道。
第 15 行,将匿名函数并发执行。
第 18 行,用循环生成 3 到 0 之间的数值。
第 21 行,将 3 到 0 之间的数值依次发送到通道 ch 中。
第 24 行,每次发送后暂停 1 秒。
第 30 行,使用 for 从通道中接收数据。
第 33 行,将接收到的数据打印出来。
第 36 行,当接收到数值 0 时,停止接收。(如果继续发送,由于接收 goroutine 已经退出,没有 goroutine 发送到通道,因此运行时将会触发宕机报错。)

接收方代码也可以写成下面这个样子:

// 开始无限循环等待数据
for {
    // 从channel中获取一个数据
    data := <-c
    fmt.Println(data)
    
    if data == 0 {
        break
    }
}

单向通道的使用

package main

import (
	"fmt"
)

func main() {
	ch := make(chan int)
	var chSendOnly chan<- int = ch
	var chRecvOnly <-chan int = ch
	go func() {
		chSendOnly <- 1	
	}()
	fmt.Println(<-chRecvOnly)
	close(ch)
}

使用select+channel

select会阻塞直到其中一个case可以执行(或者有default的话会执行default)。通过在case中加入超时机制,可以让channel在没有后续操作时超时返回。

package main
import (
    "fmt"
    "time"
)
func main() {
    ch := make(chan int)
    quit := make(chan bool)
    //新开一个协程
    go func() {
        for {
            select {
            case num := <-ch:
                fmt.Println("num = ", num)
            case <-time.After(3 * time.Second):
                fmt.Println("超时")
                quit <- true
            }
        }
    }() //别忘了()
    for i := 0; i < 5; i++ {
        ch <- i
        time.Sleep(time.Second)
    }
    <-quit // 阻塞,等待匿名函数超时返回
    fmt.Println("程序结束")
}

输出为1.

channel基本使用

创建channel

创建普通channel

ch1 := make(chan int)  // 创建一个整型类型的通道
ch2 := make(chan interface{})  // 创建一个空接口类型的通道, 可以存放任意格式
type Equip struct{ /* 一些字段 */ }
ch2 := make(chan *Equip)

创建单向channel
有时候处于安全需求需要限制channel的读写范围,可以将channel的读和写单独声明出来,这样向【读channel】中写入数据以及从【写channel】中读取数据就是非法的。

ch := make(chan int)
// 将该ch的输入端封装为chSendOnly,只能向chSendOnly写入数据
var chSendOnly chan<- int = ch
// 将该ch的输出端封装为chRecvOnly,只能向chRecvOnly写入数据
var chRecvOnly <-chan int = ch

创建buffered channel

package main
import "fmt"
func main() {
    // 创建一个3个元素缓冲大小的整型通道
    ch := make(chan int, 3)
    // 查看当前通道的大小
    fmt.Println(len(ch))
    // 发送3个整型元素到通道
    ch <- 1
    ch <- 2
    ch <- 3
    // 查看当前通道的大小
    fmt.Println(len(ch))
}

当buffered channel的缓冲区填满时,写入操作会阻塞;
当buffered channel的缓冲区为空时,读操作会阻塞;

接收channel的数据

// 阻塞式 
data := <-ch
// 非阻塞式
data, ok := <-ch
// 循环式:会不断从信道中读取数据,直到信道关闭
for i := range ch {
	fmt.Println(i)
}

使用阻塞式会一直等到通道传输数据给变量data后再继续往下执行;一般阻塞式使用场景会比较少,因为比较占CPU。
使用非阻塞式如果channel内没有数据,语句会继续执行;此时data为channel数据类型的零值,ok为false(表示未接收到数据)。

关闭channel

关闭channel
一般情况信道是不用关闭的,除非需要通知接收方(例如接收方在一个range循环里)。

close(ch)  // 只有发送者可以关闭信道

判断channel是否已经关闭

x, ok := <-ch

ok为false时表示channel已经关闭

管道的数据类型

理论上,什么东西都可以通过channel传输

function channel

func abstractListener(fxChan chan func() string ) {
	fxChan <- func() string {
 	return "Sent!"
	}
}
func main() {
 fxChan := make (chan func() string)
 defer close(fxChan)
 go abstractListener(fxChan)
 select {
 case rfx := <- fxChan:
 msg := rfx()
 fmt.Println(msg)
 fmt.Println("Received!")
 }
}

interface channel

type Messenger interface {
 Relay() string
}
type Message struct {
 status string
}
func (m Message) Relay() string {
 return m.status
}
func alertMessages(v chan Messenger, i int) {
 m := new(Message)
 m.status = "Done with " + strconv.FormatInt(int64(i),10)
 v <- m
}
func main () {

 msg := make(chan Messenger)
 for i:= 0; i < 10; i++ {
 go alertMessages(msg,i)
 }
 select {
 case message := <-msg:
 fmt.Println (message.Relay())
 }
 <- msg
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值