• Go语言基础之并发 goroutine chan


    参考文档:
    https://www.liwenzhou.com/posts/Go/14_concurrence/
    http://www.5lmh.com/并发编程/channel.html

    示例一:

    package main
    
    import (
    	"fmt"
    	"sync"
    )
    var wg sync.WaitGroup
    func main() {
    	wg.Add(2)
    	go nobufChannel() //不带缓冲区的初始化
    	go bufChannel()   //有缓冲区的通道
    	wg.Wait()
    }
    
    //不带缓冲区的初始化
    func nobufChannel() {
    	defer wg.Done()
    	channel1 := make(chan int)
    	go func() {
    		x := <-channel1
    		fmt.Println("channel里取出的数字:", x)
    		close(channel1)
    	}()
    	channel1 <- 10
    	fmt.Println("10存入channel")
    
    }
    
    //有缓冲区的通道
    func bufChannel() {
    	defer wg.Done()
    	channel2 := make(chan int, 1) //指定了只能放一个数
    	channel2 <- 10
    	fmt.Println("10 发送到通道用bufChannel")
    	//channel2 <- 20 //todo 指定了只能放一个数,取出后,才能继续向通道里存入数据
    	//fmt.Println("20 发送到通道用bufChannel")
    	ch2 := <-channel2
    	fmt.Println("取出bufChannel中的数据,", ch2)
    }
    
    

    示例二:

    package main
    import (
    	"fmt"
    	"sync"
    )
    var wg sync.WaitGroup
    var once sync.Once
    func main() {
    	fmt.Println("我是返回值")
    	a := make(chan int, 100)
    	b := make(chan int, 100)
    	wg.Add(3)
    	go setValue(a)
    	go getValue(a, b)
    	go getValue(a, b)
    	for ret :=range b{
    		fmt.Println(ret) //循环打印b通道中的数据
    	}
    	wg.Wait()
    }
    func setValue(a chan int) {
    	defer wg.Done()
    	//循环的存入通道中
    	for i := 1; i <= 100; i++ {
    		a <- i
    	}
    	close(a)
    }
    
    func getValue(a, b chan int) {
    	defer wg.Done()
    	for {
    		v,ok:= <-a
    		if !ok {
    			break
    		}
    		b  <- v //循环读出a通道中的数据,再的存入b通道中
    	}
    	once.Do(func(){close(b)})
    }
    

    close()

    可以通过内置的close()函数关闭channel(如果你的管道不往里存值或者取值的时候一定记得关闭管道)

    package main
    
    import "fmt"
    
    /*
       1.对一个关闭的通道再发送值就会导致panic。
       2.对一个关闭的通道进行接收会一直获取值直到通道为空。
       3.对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
       4.关闭一个已经关闭的通道会导致panic。
    
    */
    
    func main() {
    	a := make(chan int, 2)
    	a <- 10
    	a <- 20
    	close(a)
    	//for k := range a {
    	//	fmt.Println(k)
    	//}
    
    	v, ok := <-a
    	fmt.Println(v, ok) //10 true
    	v, ok = <-a
    	fmt.Println(v, ok) //20 true
    	v, ok = <-a
    	fmt.Println(v, ok) //0 false
    	v, ok = <-a
    	fmt.Println(v, ok) //0 false
    }
    

    如何优雅的从通道循环取值

    当通过通道发送有限的数据时,我们可以通过close函数关闭通道来告知从该通道接收值的goroutine停止等待。当通道被关闭时,往该通道发送值会引发panic,从该通道里接收的值一直都是类型零值。那如何判断一个通道是否被关闭了呢?

    我们来看下面这个例子:

    // channel 练习
    func main() {
        ch1 := make(chan int)
        ch2 := make(chan int)
        // 开启goroutine将0~100的数发送到ch1中
        go func() {
            for i := 0; i < 100; i++ {
                ch1 <- i
            }
            close(ch1)
        }()
        // 开启goroutine从ch1中接收值,并将该值的平方发送到ch2中
        go func() {
            for {
                i, ok := <-ch1 // 通道关闭后再取值ok=false
                if !ok {
                    break
                }
                ch2 <- i * i
            }
            close(ch2)
        }()
        // 在主goroutine中从ch2中接收值打印
        for i := range ch2 { // 通道关闭后会退出for range循环
            fmt.Println(i)
        }
    }
    

    从上面的例子中我们看到有两种方式在接收值的时候判断通道是否被关闭,我们通常使用的是for range的方式。

    通道总结

    channel常见的异常总结,如下图:

    注意:关闭已经关闭的channel也会引发panic。

  • 相关阅读:
    [书目20071127]图书 时间陷阱 目录
    [文摘20071113]十四项心理定律
    Win2003下:JDK1.5 + Eclipse3.2 + Tomcat6.0 + tomcatPluginV32
    [转]初试eclipse mysql
    [转]测试工具
    [转]如何进行软件需求分析
    OA流程设计尝试:Div步骤拖动
    [转]需求分析的20条法则
    Eclipse 3.2 + Tomcat 5.5 + Lomboz 3.2 简单配置
    [转]软件项目管理中的风险管理研究
  • 原文地址:https://www.cnblogs.com/haima/p/12198891.html
Copyright © 2020-2023  润新知