golang管道

golang中的channel

channel用于goroutine之间的通讯golang

若是不用channel,使用共享全局变量的方式,须要加锁安全

// synchornized 同步
// golang中的 sync包中有互斥锁 
var lock sync.Mutex  // mutex 互斥

lock.Lock() // 上锁
// 多个goroutine同时对相同的数据进行修改
lock.Unlock() // 解锁

使用同步锁并发效率会很低数据结构

channel主要用于goroutine通讯和解决主线程等待goroutine执行结束再退出的问题并发

basic concept of channel

  1. 本质上是一个FIFO的数据结构-队列
  2. 线程安全,不须要加锁
  3. channel是有类型的,如字符串channel chan string,只能保存string数据

declare channel

var variableName chan dataType函数

var c1 chan int
var c2 chan bool
var c3 chan map[int]string

channel 是引用类型,必须初始化后才能使用线程

var c1 chan int
c1 = make(chan int, 4) // 使用make函数初始化chan int, 4是容量
// 初识化以后容量不可动态变化,channel不会自动扩容,写入数据时不能超过容量,不然会deadlock
// fatal error: all goroutines are asleep - deadlock!

c1 <- 666  // 写入数据

num := <- c1 // 取出数据, 每取一个数据,channel长度减一
// 不使用goroutine的状况下   
// 当数据所有取出是再对channel取数据会报 deadlock

使用channel的注意事项

  1. 只保存指定类型数据
  2. 数据满后,不能在添加数据,不然deadlock
  3. 从channel中取出数据,长度减一,腾出一个位置可添加数据
  4. 不使用goroutine的状况下,当数据所有取出是再对channel取数据会报 deadlock

channel的遍历和关闭

内置函数close(channel) 能够关闭channel,不能再添加数据,可是能够读取数据code

channel能够用for range方式遍历协程

可是必须是已关闭的channel队列

for range遍历一个未关闭的channel会出现deadlock字符串

c1 := make(chan int, 10)
for i:=0; i<10; i++ {
    c1 <- i
}
close(c1)

for v := range c1 {
    fmt.Println(v)
}

对于一个已关闭的channel读取数据时,若是数据已所有取出,取值状态返回false而不会报错

func main() {
    c1 := make(chan int, 2)
    c1 := make(chan int, 2)
    c1 <- 1
    c1 <- 2
    c2 <- 1
    c2 <- 2
    close(c1)
    <- c1  // 取出值 丢弃不用 1
    <- c1  // 2
    v, ok := <-c1  // 数据已取完
    fmt.Println(v,ok) // 0  false
    
    <- c2  // 1
    <- c2  // 2
    v, ok := <-c2  // 数据已取完 程序到这里会出错  deadlock
    fmt.Println(v,ok) 
}

对于已关闭的管道,能够在取完数据时结束等待goroutine执行

for {
    _, ok := <- exitChan bool
    if !ok {
        break
    }
}

未关闭的channel须要取出指定个数的值以后结束等待goroutine执行

channel使用细节

channel默认是既能够读又能够写的

能够声明为只读或只写

var c1 <- chan int // 只读
var c2 chan <- int  // 只写

通常channel不会声明为只读和只写,而是在声明函数形参的时候使用

var c1 chan string

func sendData(c chan <- string) {...}

func readData(c <- chan string) {...}

// c1是默承认读可写的channel
// 当c1做为参数传给 sendData时 在 sendData 函数中只容许对管道进行写操做
// 当c1做为参数传给 readData时 在 readData 函数中只容许对管道进行读操做

goroutine中使用recover函数解决协程中出现的panic ,不影响主线程的执行

defer func() {
    if err = recover(); err != nil {
        ...dosomething
    }
}

select

select用于解决从管道中取数据的阻塞问题

不使用select,在遍历未关闭的管道时会deadlock

然而,不少状况下管道都是未关闭的,由于很差肯定何时关

用select来遍历未关闭管道,不会deaklock

func main() {
    c1 := make(chan int, 10)
    c2 := make(chan string, 5)
    for i := 0; i < 10; i++ {
        rand.Seed(time.Now().UnixNano())
        c1 <- rand.Intn(100)
    }
    c2 <- "commerce"
    c2 <- "corresponding"
    c2 <- "oblige"
    c2 <- "decline"
    c2 <- "praise"

    label1:
    for {
        select {
        case v := <-c1:
            fmt.Println(v)
        case v := <-c2:
            fmt.Println(v)
        default:
            fmt.Println("两个管道都没数据了吧")
            break label1
        }
    }

    fmt.Println("adhere")
}
相关文章
相关标签/搜索