# Go学习-Day9

文章目录

Channel

  • Channel本质是一个队列
  • 多goroutine访问时不需要加锁,Channel天然线程安全
  • channel有类型,只能写入相同类型
  • channel是引用类型
  • channel必须初始化才能写入数据,make分配内存

声明

go 复制代码
	var intChan chan int
	intChan = make(chan int, 3)
  • java不是很熟悉,感觉chan有点像java的原子类

存入取出

go 复制代码
intChan<- xxx //存入
a := <= intChan//取出
  • 管道不会自然增长,不能超过容量,不能从空的管道里取出数据,会上DeadLock

  • 如果想要存储任意类型的管道,可以用空借口

go 复制代码
var allChan chan interface{}
  • 但是,取出的时候注意类型断言

go 复制代码
close(intChan)
  • channel关闭之后就不能再写入了,但是能继续读出

  • 关闭之后能用for-range来遍历,如果不关闭的话会出现死锁

  • 死锁的情况很多,建议多找几篇文章看看,写写实操一下

  • 空的缓冲chan相当于无缓冲的chan,无缓冲的chan需要接收者,传入者,否则就会死锁,注意及时关闭

  • 只向管道内写入,不读取就会deadlock,读得慢没有关系

  • 关键是要给每个管道安排一个发送者,和接收者!!!

一个简单的死锁分析

go 复制代码
package main

import (
	"fmt"
	"time"
)

func write(intChan chan int) {
	for i := 0; i < 5; i++ {
		fmt.Println("写入: ", i)
		intChan <- i
		time.Sleep(time.Second)
	}
	//close(intChan)
}

func read(intChan chan int, exitChan chan bool) {
	for {
		val, ok := <-intChan
		if !ok {
			break
		}
		fmt.Println("读到", val)
	}
	exitChan <- true
	close(exitChan)
}
func main() {

	intChan := make(chan int, 20)
	exitChan := make(chan bool, 1)
	go write(intChan)
	go read(intChan, exitChan)

	for {
		_, ok := <-exitChan
		if !ok {
			break
		}
	}
}
  • 输出
shell 复制代码
写入:  0
读到 0
写入:  1
读到 1
写入:  2
读到 2
写入:  3
读到 3
写入:  4
读到 4
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        E:/JetBrains/GoLandSpace/src/go_code/project01/main/hello.go:36 +0xe8

goroutine 7 [chan receive]:
main.read(0x0?, 0x0?)
        E:/JetBrains/GoLandSpace/src/go_code/project01/main/hello.go:19 +0x99
created by main.main
        E:/JetBrains/GoLandSpace/src/go_code/project01/main/hello.go:33 +0xd9

Process finished with the exit code 2
  • 下面是个人的分析,不一定对,有大佬可以来指正
  • 如果我们不close,channel是可以读的,我们可以边读,边写,并且,读的速度是可以更慢或者更快的,go底层会通过上下文自行判断。
  • 但是这里,我们写的协程,我们关闭channel,在程序运行完之后自行关闭,此时我们读的协程会卡在intChan,等待读入,但是此时还不会报错,因为协程会因为主线程结束而结束。但是后面的exitChan会导致报错
go 复制代码
package main

import (
	"fmt"
	"time"
)

func write(intChan chan int) {
	for i := 0; i < 5; i++ {
		fmt.Println("写入: ", i)
		intChan <- i
		time.Sleep(time.Second)
	}
	//close(intChan)
}

func read(intChan chan int, exitChan chan bool) {
	for {
		val, ok := <-intChan
		if !ok {
			break
		}
		fmt.Println("读到", val)
	}
	fmt.Println("到了这里")
	//exitChan <- true
	//close(exitChan)
}
func main() {

	intChan := make(chan int, 20)
	exitChan := make(chan bool, 1)
	go write(intChan)
	go read(intChan, exitChan)

	time.Sleep(time.Second * 10)
	//for {
	//	_, ok := <-exitChan
	//	if !ok {
	//		break
	//	}
	//}
}
  • 这样并没有报错,并且发现到了这里没有打印,说明read函数作为intChan的接收者一直在等待,这时候。

  • 但是,主线程运行到下面的for的时候,此时exitChan是空的,因为intChan一直在死循环等待,所以触发了死锁

  • 只读只写

go 复制代码
var chanIn chan<- int//只写
go 复制代码
var chanOut <-chan int//只读
  • select {case ...}可以安全地取出数据

  • 使用recover捕获协程终端 panic

相关推荐
Funny_AI_LAB2 小时前
AI Agent最新重磅综述:迈向高效智能体,记忆、工具学习和规划综述
人工智能·学习·算法·语言模型·agi
代码游侠3 小时前
学习笔记——Linux内核与嵌入式开发1
linux·运维·前端·arm开发·单片机·嵌入式硬件·学习
宇钶宇夕3 小时前
CoDeSys入门实战一起学习(二十八):(LD)三台电机顺起逆停程序详解—上升、下降沿使用上
单片机·嵌入式硬件·学习
科技林总3 小时前
【系统分析师】6.5 电子商务
学习
代码游侠3 小时前
C语言核心概念复习(一)
c语言·开发语言·c++·笔记·学习
tb_first3 小时前
万字超详细苍穹外卖学习笔记1
java·jvm·spring boot·笔记·学习·tomcat·mybatis
今儿敲了吗3 小时前
10| 扫雷
c++·笔记·学习
Grassto3 小时前
11 Go Module 缓存机制详解
开发语言·缓存·golang·go·go module
崇山峻岭之间3 小时前
Matlab学习记录41
学习
觉醒大王3 小时前
科研新手如何读文献?从“乱读”到“会读”
论文阅读·笔记·深度学习·学习·自然语言处理·学习方法