golang channel介绍及无缓冲channel示例

一、channel

channel,可译为通道,是go语言协程goroutine之间的通信方式。
channel通信可以想象成从管道的一头塞进数据,从另一头读取数据。
协程通过channel通信可以不用进行加锁操作。

1.创建channel

channel是拥有数据类型的,channel只能传递指定的数据类型的值。

  • 通过make创建channel

语法:

c := make(chan 数据类型)

例子:

// 创建int类型的channel
c := make(chan int)

2.读取channel中的数据

// 从channel变量c中读取数据,保存到变量v中
v := <-c

// 从channel变量c中读取数据,数据直接丢弃
<-c

提示:如果channel中没有数据,会阻塞协程,直到channel有数据或者channel关闭。

3.往channel中写数据

// 往channel变量c中,写入int数据100
c <- 100

channel有两种类型:

  • 无缓冲channel
  • 缓冲channel

无缓冲的意思就是channel只能保存一个数据,当往无缓冲的channel写入第2个数据的时候协程会被阻塞,直到channel中的第1个数据被取走,才会唤醒被阻塞的协程。

缓冲channel,指的是channel中有一个缓冲队列,当写入的数据没有塞满这个缓冲队列之前,往channel写数据协程是不会被阻塞的,如果取数据的速度比写数据的速度快,那么永远不会阻塞写操作。

是否缓冲channel,写操作上区别就是什么时候会阻塞写操作。

二、无缓冲channel应用示例

1.示例1

package main

import "fmt"

// 定义累加函数,负责将s数组所有值相加, sum函数还接受一个int类型的channel参数c
func sum(s []int, c chan int) {
    sum := 0
    // 累加数组s的所有值
    for _, v := range s {
        sum += v
    }

    // 将计算的结果发生到channel中
    c <- sum
}

func main() {
    s := []int{7, 2, 8, -9, 4, 0}

    // 定义一个int类型channel,用来接收协程计算结果
    c := make(chan int)

    // 创建第1协程,计算数组前半部分的累加值
    go sum(s[:len(s)/2], c)

    // 创建第2个协程,计算数组后半部分的累加值
    go sum(s[len(s)/2:], c)

    // 通过channel接收,两个协程的并发计算结果
    // 这里读取两次channel
    x := <-c
    y := <-c

    // 打印计算结果
    fmt.Println(x, y, x+y)
}

运行结果如下:

-5 17 12

这个例子通过channel将子协程中的计算结果回传给主协程,根据channel的特性,如果子协程的计算还没有完成,不会给channel发送数据,主协程读取channel的操作会一直阻塞,直到收到数据为止。这样也就避免了子协程还没执行完,主协程就退出的问题。

2.示例2

package main

import (
	"fmt"
	"time"
)

func NoBufChannel() {
	//创建一个无缓存的channel
	ch := make(chan int, 0)

	//len(ch)缓冲区剩余数据个数, cap(ch)缓冲区大小,两者这里永远都是0
	fmt.Printf("缓冲区剩余数据个数:%d, 缓冲区大小:%d\n", len(ch), cap(ch))

	//新建协程
	go func() {
		for i := 0; i < 3; i++ { //写三次
			fmt.Printf("准备写入第【%d】个子协程\n", i)
			ch <- i //往chan写内容
			//fmt.Printf("len(ch) = %d, cap(ch)= %d\n", len(ch), cap(ch))
			fmt.Printf("上面是第【%d】个子协程的写入,我这里会执行到吗\n", i)
		}
	}()

	//延时2秒
	fmt.Printf("我要去睡两秒\n")
	time.Sleep(2 * time.Second)
	fmt.Printf("睡醒了\n")

	for i := 0; i < 3; i++ { //必须读三次
		fmt.Printf("此时开始读第【%d】个写入\n", i)
		num := <-ch //读管道中内容,没有内容前,阻塞
		fmt.Printf("第【%d】个写入读到了,值是【%d】\n", i, num)
	}
}

func main() {
	// 无缓冲通道
	NoBufChannel()

}

运行结果如下:

缓冲区剩余数据个数:0, 缓冲区大小:0
我要去睡两秒
准备写入第【0】个子协程
睡醒了
此时开始读第【0】个写入
第【0】个写入读到了,值是【0】
此时开始读第【1】个写入
上面是第【0】个子协程的写入,我这里会执行到吗
准备写入第【1】个子协程
上面是第【1】个子协程的写入,我这里会执行到吗
准备写入第【2】个子协程
第【1】个写入读到了,值是【1】
此时开始读第【2】个写入
第【2】个写入读到了,值是【2】

三、go 缓冲channel示例

四、goroutine channel应用select语句

你可能感兴趣的:(go,goroutine,无缓冲channel,缓冲channel,select)