Go语言的channel和select语句

文章目录

      • 定义channel,channel类别
      • 无缓冲通道的同步通信
      • for 和 for range 两种方式从channel中读数据
      • select语句

定义channel,channel类别

var ch chan int
ch1 := make(chan int)   // 创建一个无缓冲通道,缓冲区大小为0
ch2 := make(chan bool, 5)  // 创建一个缓冲区大小为5的通道
ch1 <- data  // 向通道中写数据
data <- ch1  // 从通道中读数据
fmt.Println(len(ch1), cap(ch1)) // 0  0
fmt.Println(len(ch2), cap(ch2)) // 0  5

// 还有一种单向通道:
ch3 := make(chan int)
ch4 := make(chan <- int)  // 单向,只能写,不能读
ch5 := make(<- chan int)  // 单向,只能读,不能写

/*
非缓冲通道:make(chan T)
		一次发送,一次接收,都是阻塞的
缓冲通道:make(chan T, capacity)
		发送:缓冲区容量满了,才会阻塞
		接收:缓冲区没有数据量,才会阻塞
	注意,通道的读取和队列类似,先进先出
*/

注意,chan初始声明为nil,是引用类型进行传递

package main

import "fmt"

func main() {
	var a chan int  // 只是声明,值为nil
	fmt.Printf("%T, %v\n", a, a)  // chan int   nil

	if a == nil {
		fmt.Println("channel是nil的, 不能使用, 需要先创建通道。。。")
		a = make(chan int)
		fmt.Println(a)  // 0xc000102060
	}
	test1(a)     // 说明chan传的是引用类型
}

func test1(ch chan int) {
	fmt.Printf("%T, %v\n", ch, ch)   // chan int     0xc000102060
}

无缓冲通道的同步通信

package main

import (
	"fmt"
	"time"
)

func main() {
	c := make(chan int) // 创建无缓冲的通道 c

	// 内置函数 len 返回未被读取的缓冲元素数量,cap 返回缓冲区大小
	fmt.Printf("len(c)=%d, cap(c)=%d\n", len(c), cap(c))

	go func() {
		defer fmt.Println("子go程结束")

		for i := 0; i < 6; i++ {
			c <- i // 往通道c中写数据
			fmt.Printf("子go程正在运行[%d]: len(c)=%d, cap(c)=%d\n", i, len(c), cap(c))
		}
	}()

	time.Sleep(2 * time.Second) //延时2s

	for i := 0; i < 6; i++ {
		num := <-c // 从c中读数据,并赋值给num
		fmt.Println("num = ", num)
	}

	fmt.Println("main进程结束")
}

打印结果如下:
Go语言的channel和select语句_第1张图片

for 和 for range 两种方式从channel中读数据

package main

import "fmt"

// 用for range访问通道更方便
func main() {
	ch1 := make(chan int)
	go sendData(ch1)
	// for循环的for range, 来访问通道
	for v := range ch1 { // 相当于是从通道中读数据, v <- ch1
		fmt.Println("read data:", v)
	}
	fmt.Println("main over...")

	/* 和 for range读取数据效果相同。
		for {
			v, ok := <-ch1
			if !ok {
				fmt.Println("已经读取了所有数据。。", ok)
				break
			}
			fmt.Println("读取数据:", v)
		}
	*/

}

func sendData(ch1 chan int) {
	for i := 0; i < 10; i++ {
		ch1 <- i
	}
	close(ch1) 
	// 关闭通道。如果不关闭通道,那么主goroutine就会发生阻塞,
	// 一直等待读数据,死锁.
}

select语句

package main

import (
	"fmt"
	"time"
)

/*
select是go的一个控制结构,select语句类似于switch语句,
select会随机执行一个可运行的case,如果没有case可以运行,它将阻塞,直到没有case可以运行。
select的case条件都是一个channel,通信
*/

func main() {

	ch1 := make(chan int)
	ch2 := make(chan int)

	go func() {
		time.Sleep(2 * time.Second)
		ch1 <- 100
	}()

	go func() {
		time.Sleep(2 * time.Second)
		ch2 <- 200
	}()

	select {
	case num1 := <-ch1:
		fmt.Println("读取ch1的数据:", num1)
	case num2, ok := <-ch2:
		if ok {
			fmt.Println("读取ch2的数据:", num2)
		} else {
			fmt.Println("ch2通道已经关闭")
		}
		// default:
		// 	fmt.Println("执行了default。。。")
	}
}

加上default语句时,case都被阻塞,执行了defalut。
没有加default时,case都可以运行,此时select随机选择进入一个case运行
Go语言的channel和select语句_第2张图片

你可能感兴趣的:(golang基础,go)