版权声明:如有需要,可供转载,但请注明出处:https://blog.youkuaiyun.com/City_of_skey/article/details/86263168
目录
1、channel简介
go语言中并发程序非常简单直接用关键字go就可以起一个协程,go语言的协程是语言级别的所以它的开销比线程、进程要小,因为线程和进程是系统级别的并发。go语言协程同步可以通过channel来解决。
channel有三种类型:
var aa chan int /*可读可写*/
var aa chan <- int /*只能写*/
var aa <- chan int /*只能读*/
channel类型必须先用make初始化再使用
aa := make(chan int)
关闭channel用函数close,一个关闭的channel还可以去读数据,但不能在写如数据,如果写入数据就会产生panic
aa := make(chan int)
close(aa)
aa <- 4 /*panic*/
2、channel阻塞
对于buffer为1的channel,从channel中读数据时当没有读到就会阻塞,向channel中写数据如果没有空间存储也会阻塞。
读阻塞:
aa := make(chan int)
b := <- aa /*aa中没有数据就会阻塞*/
写阻塞:
aa := make(chan int)
aa <- 4
aa <- 5 /*4还没有取出来,channel中没有空间, 阻塞*/
3、channel非阻塞
当我们需要非阻塞的channel我们可以给channel分配空间,当空间没有填满channel不会阻塞,有buffer的channel相当于一个队列,遵循先进先出的规则。
aa := make(chan int, 4)
带buffer的channel读取可以用range。但要注意的是当channel中的数据被取完后就会阻塞,如果没有再往channel中写数据就会产生死锁
w := make(chan int, 4)
w <- 4
w <- 5
for b := range w {
/*最后一个5取走后就会阻塞,产生死锁*/
fmt.Printf("w:%d\n", b)
}
避免死锁方法:
每次range读取数据后判断channel的长度,如果是0就说明channel为空,就应该退出。
w := make(chan int, 4)
w <- 1
w <- 2
w <- 3
w <- 4
for b := range w {
fmt.Printf("w:%d\n", b)
/*如果channel长度是0就退出循环,不然会阻塞产生死锁*/
if len(w) <= 0 {
break
}
}
4、select
go中的select相当于c语言中的switch,是对对个channle做选择,它不是循环,如果要循环判断就要在外面在加一个for
package main
import (
"fmt"
"time"
)
func main() {
a := make(chan int)
b := make(chan int)
defer close(a)
defer close(b)
go func() {
for i := 1; i < 3; i++ {
a <- i
b <- (i + 1)
time.Sleep(1 * time.Second)
}
}()
for {
select {
case val1 := <-a:
fmt.Println("val1:", val1)
case val2 := <-b:
fmt.Println("val2:", val2)
case <-time.After(3 * time.Second): /*3秒超时*/
fmt.Println("time out quit")
return
}
}
}
select中可以加一个超时时间,超过这个时间通道中还没有数据就直接返回。