zoukankan      html  css  js  c++  java
  • [go]channel

    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
    		}
    	}
    }
    
  • 相关阅读:
    [vp]ARC068
    [vp]ARC067
    Vision transformer
    rosetta使用silent格式储存PDB结构,节省本地存储
    CentOS7下安装JDK详细过程
    jdk下载Oracle共享账号
    虚拟机地址发生变化
    字节跳动面试题,给你一个每一项都是数值混乱顺序的数组,只要里面正确顺序的值输出。如[5,1,3,6,2,7],只要[1,2,7]
    spring boot web 第一个项目新建
    xmind-excel
  • 原文地址:https://www.cnblogs.com/iiiiiher/p/12252412.html
Copyright © 2011-2022 走看看