1.为什么设计成线程安全?
不同协程通过channel 通信,本身使用场景就是多线程,为了保证数据一致性,必须实现线程安全。
2.如何实现线程安全的?
channel底层的实现中,hchan结构体中采用Mutex锁来保证数据读写安全,在对循环数组buf中的数据进行入队和出队的操作时,必须先获取互斥锁,才能操作channel数据。
3.nil、关闭的 channel、有数据的 channel,再进行读、写、关闭会怎么样?
注意:以下所有情况都是在goroutine中;因为在主程序中没有阻塞一说,会直接死锁
channel的两种类型——有缓冲,无缓冲
|
|
无缓冲 | 有缓冲 |
| 创建方式 | make(chan int) |
make(chan int , 2) |
| 发送阻塞 |
数据接收前发送阻塞 |
缓冲区满时发送阻塞 |
| 接受阻塞 |
数据发送前接收阻塞 |
缓冲区空时接收阻塞 |
无缓冲
时刻都是同步状态
无缓冲chan必须有发送G/接收G的同时有接收G/发送G,否则就会阻塞, 报错:fatal error: all goroutines are asleep - deadlock!
package main
import (
"fmt"
"time"
)
func loop(ch chan int) {
for {
select {
case i := <-ch:
fmt.Println("this is value of unbuffer channel", i)
}
}
}
func main() {
ch := make(chan int)
ch <- 1
go loop(ch)
time.Sleep(1 * time.Millisecond)
}
但如果把ch <-放到go loop(ch)下面,程序就会正常运行
有缓冲
package main
import (
"fmt"
"time"
)
func loop(ch chan int) {
for {
select {
case i := <-ch:
fmt.Println("this is value of unbuffer channel", i)
}
}
}
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
ch <- 4
go loop(ch)
time.Sleep(1 * time.Millisecond)
}
也会报错:fatal error: all goroutines are asleep - deadlock!
是因为channel的大小为3,而要往里面塞4个数据,所以会阻塞住
解决方法:
1.把channel的长度调大(不是很好的解决方案)
2.把发送者ch<-1等代码移动到go loop(ch)下面,让channel实时消费就不会导致阻塞了
channel 有三种模式:只写操作模式(单向通道)、只读操作模式(单向通道)、读写操作模式(单向通道)
|
|
写模式 | 读模式 | 读写模式 |
| 创建 | make(chan<- int) |
make(<-chan int) |
make(chan int) |
channel 有三种状态:未初始化、正常、关闭
|
|
未初始化 | 关闭 | 正常 |
| 关闭 | panic | panic | 正常关闭 |
| 发送 | 永远阻塞导致死锁 |
panic |
阻塞或成功发送 |
| 接收 |
永远阻塞导致死锁 |
缓冲区为空则为零值,否则可以继续读 | 阻塞或成功接收 |
注意点:
1.一个channel不能多次关闭或导致panic。
2.如果多个goroutine都监听同一个channel,那么channel上的数据都可以随机被任何一个goroutine取走进行消费。
3.如果多个goroutine都监听同一个channel,那么这个channel被关闭,则所有goroutine都能收到退出信号。
注意:以上所有情况都是在goroutine中;因为在主程序中没有阻塞一说,会直接死锁
