• channel的基本使用


    1、管道分类

    • 读写管道
    • 只读管道
    • 只写管道
    • 缓冲通道 :创建时指定大小(如果不指定默认为非缓冲通道)

    2、正确使用管道

    1. 管道关闭后自能读,不能写

    2. 写入管道不能超过管道的容量cap,满容量还写则会阻塞

    3. 管道为空时,如果没有关闭,则继续读取会阻塞当前线程,直到有东西写入管道

    4. m,ok:=<-intChan  //ok用来检测产是否已经关闭 false代表关闭了,关闭了m就是默认值
      

      一般如下操作才可以判断读取是否完毕,如果写进程没有关闭管道则说明还有东西要写

      v,ok:=<-intChan
      if !ok{
      	fmt.Println("读取完毕")
      	break
      }
      

    3、管道遍历与访问

    • for-range访问
    • select访问(常用)

    for-range

    //gor-range  如果管道没有数据同时还没有关闭  则会一直阻塞等待
    	go func() {
    		for i:=range c{ //注意这里没有ok判断是否关闭了 只有一个返回值
    			fmt.Println(i)
    		}
    	}()
    

    下面这种情况for-range会阻塞,1秒后打印1,和 “关闭了”,如果没写同时又没有关闭,那么则会一直阻塞

    	ch := make(chan int)	
    	go func() {
    		time.Sleep(time.Second)
    		//close(ch)
    		ch<-1
    	}()
    	go func() {
    	for i:=range ch{
    		log.Println(i)
    	}
       log.Println("关闭了")
    	}()
    	time.Sleep(time.Hour)
    

    下面的这种情况,for-range在一秒后会结束 ,同时打印“关闭了

    	ch := make(chan int)	
    	go func() {
    		time.Sleep(time.Second)
    		close(ch)
    		//ch<-1
    	}()
    	go func() {
    		for i:=range ch{
    			log.Println(i)
    		}
    		log.Println("关闭了")
    	}()
    	time.Sleep(time.Hour)
    

    select

    select会随机选择case里面没有阻塞的管道进行读取或写入,如果都阻塞则执行default语句,一般select都会伴随一个default语句

    	c:=make(chan string,2)
    	send:= func(v string) {
    		select {
    		case c<-v:log.Println("输入",v)
    		default:log.Println("缓冲区已经满")
    		}
    	}
    
    	receive:= func() string {
    		select {
    		case v:=<-c:return v
    		default:
    			log.Println("缓冲区空")
    			return ""
    		}
    	}
    
    
    	send("h1")
    	send("h2")
    	send("h3") //输入失败
    
    	log.Println(receive())
    	log.Println(receive())
    	log.Println(receive()) //取失败
    

    当管道关闭时,select也会执行成功,如下打印了一秒‘等' 之后就会结束,这里没有往管道写东西,所以结束时打印的是默认值0

    	ch := make(chan int)
    	//go func() {
    	//	ch <- 1
    	//}()
    	go func() {
    		time.Sleep(time.Second)
    		close(ch)
    		
    	}()
    	go func() {
    		for {
    			time.Sleep(time.Microsecond*500)
    			select {
    			case v := <-ch:
    				log.Println(v)
    				return
    			default:
    				log.Println("等")
    			}
    		}
    	}()
    	time.Sleep(time.Hour)
    

    4、阻塞情况

    1、没有关闭,同时管道元素为0,读进程阻塞

    2、没有关闭,同时管道元素已经满,则写进程阻塞,无缓冲管道的话则会阻塞在写,直到有人读

    3、关闭管道,读光了还读,则读取出来的是对应类型管道的默认空值,在一个关闭通道进行写操作会报错

    	ch:=make(chan int,1)
    	ch<-1
    	close(ch)
    
    	log.Println(<-ch) //1
    	log.Println(<-ch) //0
    	log.Println(<-ch)//0
    	a,ok:=<-ch
    	log.Println(a,ok)//0 false
    
    1. 关闭管道,还写,则报错

    2. 无缓冲=堵塞,缓冲=非堵塞 无缓冲是同步,有缓冲是异步

    像下面这个无缓冲管道,没人读则一直阻塞,只会打印A1,有一个管道读走了才会继续执行写操作,就比如快递员来发快递,只会等你拿走快递才会离开,否则一直在等你来拿

    	ch := make(chan int)
    	go func() {
    		for{
    			log.Println("A1")
    			time.Sleep(time.Second)
    			//close(ch)
    			ch<-1
    			log.Println("A2")
    		}
    
    	}()
    

    那如果我把cap设置成1呢?

    	ch := make(chan int,1)
    	go func() {
    		for{
    			log.Println("A1")
    			time.Sleep(time.Second)
    			//close(ch)
    			ch<-1
    			log.Println("A2")
    		}
    
    	}()
    

    打印如下,可以写一个

    2020/03/21 20:02:59 A1
    2020/03/21 20:03:00 A2
    2020/03/21 20:03:00 A1
    

    其他关于通道的使用可以看这两篇博客
    go通道的常见使用场景.
    go通道的优雅关闭.


  • 相关阅读:
    开源项目
    ASP.NET上传文件带有真实的进度条
    VS2010不能调试的问题
    sql server 2005中获取数据库个数
    【转载】Nios II DMA: memory to memory
    【转】我们为什么要实习
    【转】应聘时最漂亮的回答
    【转】面试建议 每个要找工作的童鞋必看~
    【转载】FPGA Verilog HDL 系列实例 电子琴 电子钟
    【转】关于工作与生活 HP大中华区总裁孙振耀的退休感言
  • 原文地址:https://www.cnblogs.com/biningooginind/p/12545866.html
Copyright © 2020-2023  润新知