• golang管道


    golang中的channel

    channel用于goroutine之间的通信

    如果不用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,不能再添加数据,但是可以读取数据

    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")
    }
    
  • 相关阅读:
    java通过dom读写xml文件
    SWT
    Eclipse RCP学习资料
    Integration Guide
    一个Linux命令,黑客帝国
    GET和POST两种基本请求方法的区别
    贫血模式-AnemicDomainModel
    Synchronized锁在Spring事务管理下,为啥还线程不安全?
    redis 集群配置、启动
    redis cluster集群
  • 原文地址:https://www.cnblogs.com/endurance9/p/10356796.html
Copyright © 2020-2023  润新知