channel 是否线程安全?锁用在什么地方?
Golang的Channel,发送一个数据到Channel 和 从Channel接收一个数据都是原子性的。
如果把线程安全定义为允许多个goroutine同时去读写,那么golang 的channel 是线程安全的,因为channel底层数据结构中是带有lock的,不需要在并发读写同一个channe时再加锁。
而且Go的设计思想就是:不要通过共享内存来通信,而是通过通信来共享内存,前者就是传统的加锁(共享内存通信),后者就是Channel。
也就是说,设计Channel的主要目的就是在多任务间传递数据的,这当然是安全的
go channel 的底层实现原理 (数据结构)
总结hchan结构体的主要组成部分有四个:
- 用来保存goroutine之间传递数据的循环数组:buf
- 用来记录此循环数组当前发送或接收数据的下标值:sendx(发送队列)和recvx(接受队列)
- 用于保存向该chan发送和从该chan接收数据被阻塞的goroutine队列: sendq 和 recvq
- 保证channel写入和读取数据时线程安全的锁:lock
go channel为什么设计成线程安全?
不同协程通过channel进行通信,本身的使用场景就是多线程,为了保证数据的一致性,必须实现线程安全
如何实现线程安全的?
channel的底层实现中,hchan结构体中采用Mutex(互斥)锁来保证数据读写安全。在对循环数组buf中的数据进行入队和出队操作时,必须先获取互斥锁,才能操作channel数据
nil、关闭的 channel、有数据的 channel,再进行读、写、关闭会怎么样?
- 读写值 nil 管道会永久阻塞
- 关闭的管道读数据仍然可以读数据
- 往关闭的管道写数据会 panic
- 关闭为 nil 的管道会 panic
- 关闭已经关闭的管道 panic
总结:对于一个已初始化,但并未关闭的通道来说,收发操作一定不会引发 panic。但是通道一旦关闭,再对它进行发送操作,就会引发 panic。如果我们试图关闭一个已经关闭了的通道,也会引发 panic。
使用场景: 消息传递、消息过滤,信号广播,事件订阅与广播,请求、响应转发,任务分发,结果汇总,并发控制,限流,同步与异步