zoukankan      html  css  js  c++  java
  • 从两道题看go channel的用法

    在知乎看到有人分享了几道笔试题,自己总结了一下其中与channel有关的题目。全部题目在这里:

    https://zhuanlan.zhihu.com/p/35058068

    题目

    5、请找出下面代码的问题所在。

    func main() {
    	ch := make(chan int, 1000)
    	// goroutine1
    	go func() {
    		for i := 0; i < 10; i++ {
    			ch <- i
    		}
    	}()
    	
    	// goroutine2
    	go func() {
    		for {
    			a, ok := <-ch
    			if !ok {
    				fmt.Println("close")
    				return
    			}
    			fmt.Println("a: ", a)
    		}
    	}()
    	close(ch)
    	fmt.Println("ok")
    	time.Sleep(time.Second * 100)
    }
    

    8、请说出下面代码哪里写错了

    func main() {
    	abc := make(chan int, 1000)
    	for i := 0; i < 10; i++ {
    		abc <- i
    	}
    	
    	// receiver goroutine
    	go func() {
    		for {
    			a := <-abc
    			fmt.Println("a: ", a)
    		}
    	}()
    	close(abc)
    	fmt.Println("close")
    	time.Sleep(time.Second * 100)
    }
    

    解释

    首先要明确这两个要点:

    • 向一个已经关闭的channel发送数据是会抛panic的,但是从一个已关闭的channel接收数据并不会抛panic,而是得到channel数据类型的零值。
    • 同一个段程序每次启动后goruntine的调度不一定相同,所以goroutine的执行顺序有可能不一样。

    先看第一题,代码运行时会抛出panic:

    panic: send on closed channel

    这是因为goroutine1 还没把i 发送给ch,ch 就在main函数中被close,当把i 发送到ch 时就抛出了panic。同理goroutine2 中从已关闭的ch 中接收数据时ok 会返回false,然后就return了。而且由于goroutine 的执行顺序不一样,输出close、ok、panic的顺序也会不一样,有兴趣的话可以自己跑几次。
    那么如果想让程序正常输出应该怎么改呢,下面是其中一种方法:

    func main() {
    	ch := make(chan int, 1000)
    	// goroutine1
    	go func() {
    		for i := 0; i < 10; i++ {
    			ch <- i
    		}
    		
    		// 只有一个goroutine向ch发送数据,可以考虑在这个goroutine中关闭。
    		close(ch)
    	}()
    	
    	// goroutine2
    	go func() {
    		for {
    			a, ok := <-ch
    			if !ok {
    				fmt.Println("close")
    				return
    			}
    			fmt.Println("a: ", a)
    		}
    	}()
    	
    	fmt.Println("ok")
    	time.Sleep(time.Second * 100)
    }
    

    再来看第二题,运行时输出完0—9后会一直输出0,直到sleep的时间结束。这是因为在把0—9发送给abc 后abc 就被关闭了,receiver goroutine 在接收完0—9后没有跳出for 循环,而是一直从被关闭的abc 中接收数据,所以接收到的是abc 的零值——0。
    有一种最简单的办法可以改正这个程序:

    func main() {
    	abc := make(chan int, 1000)
    	for i := 0; i < 10; i++ {
    		abc <- i
    	}
    	go func() {
    	    // 使用for-range,abc被关闭后会自动退出for循环。
    		for a := range abc {
    			fmt.Println("a: ", a)
    		}
    	}()
    	close(abc)
    	fmt.Println("close")
    	time.Sleep(time.Second * 10)
    }
    

    总结

    使用channel时,

    • 如果只有一个goroutine 向channel发送数据,可以在该goroutine 中close channel。当有多个goroutine 向channel 发送元素时close channel 的方法可以参考这篇文章:

      https://www.jianshu.com/p/d24dfbb33781

    • 用for-range 从channel 中接收数据代码简洁而且基本没有副作用。

    扩展阅读

    关于goroutine是如何调度的,可以参考这篇文章:

    https://blog.csdn.net/liangzhiyang/article/details/52669851

    References

    http://colobu.com/2016/04/14/Golang-Channels/

  • 相关阅读:
    客户端请求页面的方式和提交数据的方式
    客户端请求页面的方式
    request session application cookie 保存数据
    jsp session(会话) 的使用 cookies,application 理解
    jsp内置对象
    jsp 实现数据传递
    最近戴着眼镜坐电脑前总是不自觉的眼痛就搜了下怎么保护眼睛无意中看到了这篇文章希望广大爱好编程的朋友多注意保护自己的眼睛!!
    选择排序(使用Python描述)
    归并排序(使用Python描述)
    二分查找的两种方法(使用Python描述)
  • 原文地址:https://www.cnblogs.com/DilonWu/p/8677231.html
Copyright © 2011-2022 走看看