channel类型数据结构
- chan是一个先进先出的队列(管道), 可被close
- 定义
// unbuffered chan
var ch chan int //ch指向nil
ch = make(chan int)
ch := make(chan int)
// buffered chan
ch := make(chan int, 10) // cap为10, 即chan最大容纳元素个数,代表缓存大小
- 如果cap为0, 则sender和receiver的communication会被block
- 如果cap不为0,则只有buffer满后才会被block
chan
//无缓冲(unbuffered)chan: 必须在两个goroutine之间使用,否则死锁
func main() {
ch:=make(chan int)
ch<-1
fmt.Println(<-ch)
}
//fatal error: all goroutines are asleep - deadlock!
// 无缓冲chan: 读写端都在线, 不会死锁
func main() {
ch := make(chan int)
go func() {
ch <- 1
}()
fmt.Println(<-ch)
time.Sleep(time.Second)
}
//1
//无缓冲chan: 视写端不在线, 死锁
func main() {
ch := make(chan int)
fmt.Println(<-ch)
go func() {
ch <- 1
}()
time.Sleep(time.Second)
}
//fatal error: all goroutines are asleep - deadlock!
小结: 无缓冲chan避免死锁
1.定义chan
2.另一个协程操作ch
3.本协程协同处理ch
//有缓冲(buffered)chan: 在一个goroutine也能玩
func main() {
ch:=make(chan int,1)
ch<-1
fmt.Println(<-ch)
}
//1
// 有缓冲chan
// 1.异步:不必同时在线
// 2.可控制并发
func main() {
ch := make(chan int, 1)
ch <- 3
}
- 无缓冲chan避免死锁:
//关闭chan
func main() {
ch:=make(chan int,1)
close(ch)
fmt.Println(<-ch)
}
//关闭chan
func main() {
ch := make(chan int)
go func() {
time.Sleep(time.Second)
fmt.Println("test")
close(ch)
}()
<-ch
}
//写数据(正常使用)
func main() {
ch := make(chan int)
go func() {
time.Sleep(time.Second)
fmt.Println("test")
ch <- 1
}()
<-ch
}
channel特性
// 已关闭的在关闭会pannic
func main() {
ch := make(chan int)
close(ch)
close(ch) //panic: close of closed channel
}
// 已关闭的在存值会panic
func main() {
ch := make(chan int, 2)
close(ch)
ch <- 10 //panic: send on closed channel
}
// 已关闭对取值,直到取到其零值
func main() {
ch := make(chan int)
go func() {
for i:=0;i<12;i++ {
fmt.Println(<-ch)
}
}()
ch<-10
close(ch)
time.Sleep(time.Second)
}
//10
//0
// 关闭的chanel可以读值
func main() {
ch := make(chan int)
close(ch)
fmt.Println(<-ch)
}
func main() {
ch := make(chan int)
go func() {
fmt.Println(<-ch)
}()
ch <- 10
}
//正常的chan,读写都要在线,否则阻塞等待
func main() {
ch := make(chan int)
go func() {
time.Sleep(time.Second)
ch <- 1
}()
fmt.Println(<-ch) //等待1s后输出
}
close(遍历)chan
// for遍历: 前提是必须知道数据的数量
func main() {
ch := make(chan int)
go func() {
for i := 0; i < 3; i++ {
ch <- i
}
}()
for i := 0; i < 3; i++ {
fmt.Println(<-ch)
}
}
// ok法检测
func main() {
ch := make(chan int)
go func() {
for i := 0; i < 3; i++ {
ch <- i
}
close(ch)
}()
for {
num, ok := <-ch
if ok {
fmt.Println(num)
} else {
fmt.Println("closed")
break
}
}
}
//for range遍历
func main() {
ch := make(chan int)
go func() {
for i := 0; i < 3; i++ {
ch <- i
}
close(ch)
}()
for num := range ch {
fmt.Println(num)
}
}
chan使用例子
//应用: 打印机例子
func printer(s string) {
for _, v := range s {
fmt.Printf("%c", v)
time.Sleep(time.Second / 4)
}
}
func main() {
ch := make(chan bool)
go func() {
printer("hello")
ch <- true
}()
go func() {
<-ch
printer("world")
}()
time.Sleep(time.Second * 3)
}
//控制两个协程先后顺序
func main() {
var ch = make(chan bool)
go func() {//等我结束了你在走.
for i := 0; i < 10; i++ {
fmt.Println(i)
}
ch<-true
}()
<-ch
fmt.Println("main")
}
//两个协程之间: 读协程感知关闭
func main() {
var ch = make(chan int)
go func() { //等我结束了你在走.
for i := 0; i < 5; i++ {
ch <- i
fmt.Println("zi write: ", i) //是一种io操作
}
}()
for i := 0; i < 5; i++ {
fmt.Println("main read: ", <-ch) //还没来及打印,有被抢占了
}
}
// 两个协程之间: 等待chan通知另一个协程结束
func main() {
ch := make(chan int)
go func() {
for i := 0; i < 3; i++ {
ch <- i
}
close(ch)
}()
for num := range ch { //另一个办法: num,ok:=<-ch
fmt.Println(num)
}
}
//两个协程之间: 生产者消费者
func product(ch chan int) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
}
func consumer(ch chan int) {
for v:= range ch {
fmt.Println(v)
}
}
func main() {
ch := make(chan int)
go product(ch)
go consumer(ch)
time.Sleep(time.Second)
}
//多个协程之间: main等待子协程干完活
func main() {
ch := make(chan int, 10)
//让10个人去干活
for i := 0; i < 10; i++ {
go func(i int) {
ch <- i
time.Sleep(time.Microsecond)
}(i)
}
//获取10个结果
for i := 0; i < 10; i++ {
fmt.Println(<-ch)
}
}
// 举一个有趣的列子:使用channel将异步变为同步
题目如下:
存在以下代码,请问如何实现print函数可以顺序输出1~75,要求不使用锁,只能使用channel
func main() {
for i := 1; i < 75; i++ {
go print(i)
}
time.Sleep(5*time.Second) //wait all goroutine
}
func print(i int){
}
————————————————
// - 实现
var ch = make(chan int)
func main() {
for i := 1; i < 75; i++ {
go printer(i)
ch <- i
}
time.Sleep(5 * time.Second) //wait all goroutine
}
func printer(i int) {
<-ch
fmt.Println(i)
}
死锁
//出现死锁
1.写端阻塞
func main() {
ch:=make(chan int)
ch<-1
fmt.Println(<-ch)
}
2.读阻塞
func main() {
ch:=make(chan int)
ch<-1
go func() {
fmt.Println(<-ch)
}()
}
func main() {
ch:=make(chan int)
<-ch
go func() {
ch<-1
}()
}
3.交叉死锁
func main() {
ch := make(chan int)
ch2 := make(chan int)
go func() {
for {
select {
case v := <-ch:
ch2 <- v
}
}
}()
for {
select {
case v := <-ch2:
ch <- v
}
}
}