写在前面:本篇记录实战过程中遇到的各类基础文档语法中没提到过的知识点。
1.main函数所在的goroutine就像是影分身的本体,其他的goroutine都是影分身,本体一死影分身也就全部GG了。所以main函数要留出足够的时间让其他协程运行完毕再结束。
2.,一个goroutine的栈在其生命周期开始时只有很小的栈(典型情况下2KB),goroutine的栈不是固定的,他可以按需增大和缩小,goroutine的栈大小限制可以达到1GB,虽然极少会用到这个大。所以在Go语言中一次创建十万左右的goroutine也是可以的。
3.GPM是Go语言运行时(runtime)层面的实现,是go语言自己实现的一套调度系统。P的个数是通过runtime.GOMAXPROCS设定(最大256),Go1.5版本之后默认为物理线程数。关于go的协程调度有多牛逼灵活,你可以自己找一下资料看一下就行,总结起来就是,自己的事情自己做主,不用操作系统过问管理,把自己的身体开发到极限。总之很牛逼。
4.可以手动操控运行时策略。通过runtime包,runtime.Gosched()函数是礼让一下,runtime.Goexit()函数是调头就跑。
5.单纯地将函数并发执行是没有意义的。函数与函数间需要交换数据才能体现并发执行函数的意义。提倡通过通信共享内存而不是通过共享内存而实现通信。如果说goroutine是Go程序并发的执行体,channel就是它们之间的连接。channel是可以让一个goroutine发送特定值到另一个goroutine的通信机制。Go 语言中的通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。每一个通道声明的时候需要为其指定元素类型。
6.关于关闭通道需要注意的事情是,通道是可以被垃圾回收机制回收的,它和关闭文件是不一样的,在结束操作之后关闭文件是必须要做的,但关闭通道不是必须的。
7.无缓冲的通道只有在有人接收值的时候才能发送值。 无缓冲通道上的发送操作会阻塞,直到另一个goroutine在该通道上执行接收操作,这时值才能发送成功,两个goroutine将继续执行。相反,如果接收操作先执行,接收方的goroutine将阻塞,直到另一个goroutine在该通道上发送一个值。
8.我们可以使用内置的len函数获取通道内元素的数量,使用cap函数获取通道的容量。通道满了就装不下了,就阻塞了。
9.如果你的管道不往里存值或者取值的时候一定记得关闭管道,这样往该通道发送值会引发panic,从该通道里接收的值一直都是类型零值,状态码为false,就可以判断一个通道是否被关闭了。
10.通道可以作为参数在多个任务函数间传递,通道分为单向和双向,在函数传参及任何赋值操作中将双向通道转换为单向通道是可以的,但反过来是不可以的。
11.注意当通道状态为nil【即关闭】的情况。
12.可以自行创建一个 Goroutine池 来限定针对某一功能开启的最大协程数量。
13.fmt.Printf的格式化参数。
%v
以默认的方式打印变量的值%T
打印变量的类型%+d
带符号的整型%s
正常输出字符串%p
带0x的指针%t
打印true或false
14.Timer计时器包含一个只读时间结构信道。利用信道的阻塞等待计时【到时间了,信道内才有时间数据】
time.NewTimer(10 * time.Second)开启一个定时器
timer4.Stop()关闭一个定时器。只有开启状态的定时器关闭时才会返回true,失效或已关闭的false
timer5.Reset(1 * time.Second)重置一个定时器
time.NewTicker(1 * time.Second)开启一个循环定时器,当信道内数据被取走才会执行下一次。
15.Go内置了select关键字,可以同时响应多个通道的操作,实现从多个通道接收值的需求。类似于switch语句,它有一系列case分支和一个默认的分支。每个case会对应一个通道的通信(接收或发送)过程。select会一直等待,直到某个case的通信操作完成时,就会执行case分支对应的语句。default可选,若default存在则不会等待其他case通信完成!!!
- 如果多个channel同时ready,则随机选择一个执行,不管是写入还是读取