Go 语言笔试面试题(并发编程)

举报
lxw1844912514 发表于 2022/05/10 23:28:27 2022/05/10
【摘要】 Q1 无缓冲的 channel 和 有缓冲的 channel 的区别? 答案:对于无缓冲的 channel,发送方将阻塞该信道,直到接收方从该信道接收到数据为止,而接收方也将阻塞该信道,直到发送方将数据发送到该信道中为止。 对于有缓存的 channel,发送方在没有空插槽(缓冲区使用完)的情况下阻塞,而接收方在信道为空的情况下...

Q1 无缓冲的 channel 和 有缓冲的 channel 的区别?

答案:对于无缓冲的 channel,发送方将阻塞该信道,直到接收方从该信道接收到数据为止,而接收方也将阻塞该信道,直到发送方将数据发送到该信道中为止。

对于有缓存的 channel,发送方在没有空插槽(缓冲区使用完)的情况下阻塞,而接收方在信道为空的情况下阻塞。

例如:


   
  1. func main() {
  2.     st := time.Now()
  3.     ch := make(chan bool)
  4.     go func ()  {
  5.         time.Sleep(time.Second * 2)
  6.         <-ch
  7.     }()
  8.     ch <- true  // 无缓冲,发送方阻塞直到接收方接收到数据。
  9.     fmt.Printf("cost %.1f s\n", time.Now().Sub(st).Seconds())
  10.     time.Sleep(time.Second * 5)
  11. }

   
  1. func main() {
  2.     st := time.Now()
  3.     ch := make(chan bool2)
  4.     go func ()  {
  5.         time.Sleep(time.Second * 2)
  6.         <-ch
  7.     }()
  8.     ch <- true
  9.     ch <- true // 缓冲区为 2,发送方不阻塞,继续往下执行
  10.     fmt.Printf("cost %.1f s\n", time.Now().Sub(st).Seconds()) // cost 0.0 s
  11.     ch <- true // 缓冲区使用完,发送方阻塞,2s 后接收方接收到数据,释放一个插槽,继续往下执行
  12.     fmt.Printf("cost %.1f s\n", time.Now().Sub(st).Seconds()) // cost 2.0 s
  13.     time.Sleep(time.Second * 5)
  14. }

Q2 什么是协程泄露(Goroutine Leak)?

协程泄露是指协程创建后,长时间得不到释放,并且还在不断地创建新的协程,最终导致内存耗尽,程序崩溃。常见的导致协程泄露的场景有以下几种:

缺少接收器,导致发送阻塞

这个例子中,每执行一次 query,则启动1000个协程向信道 ch 发送数字 0,但只接收了一次,导致 999 个协程被阻塞,不能退出。


   
  1. func query() int {
  2.     ch := make(chan int)
  3.     for i := 0; i < 1000; i++ {
  4.         go func() { ch <- 0 }()
  5.     }
  6.     return <-ch
  7. }
  8. func main() {
  9.     for i := 0; i < 4; i++ {
  10.         query()
  11.         fmt.Printf("goroutines: %d\n", runtime.NumGoroutine())
  12.     }
  13. }
  14. // goroutines: 1001
  15. // goroutines: 2000
  16. // goroutines: 2999
  17. // goroutines: 3998

缺少发送器,导致接收阻塞

那同样的,如果启动 1000 个协程接收信道的信息,但信道并不会发送那么多次的信息,也会导致接收协程被阻塞,不能退出。

死锁(dead lock)

两个或两个以上的协程在执行过程中,由于竞争资源或者由于彼此通信而造成阻塞,这种情况下,也会导致协程被阻塞,不能退出。

无限循环(infinite loops)

这个例子中,为了避免网络等问题,采用了无限重试的方式,发送 HTTP 请求,直到获取到数据。那如果 HTTP 服务宕机,永远不可达,导致协程不能退出,发生泄漏。


   
  1. func request(url string, wg *sync.WaitGroup) {
  2.     i := 0
  3.     for {
  4.         if _, err := http.Get(url); err == nil {
  5.             // write to db
  6.             break
  7.         }
  8.         i++
  9.         if i >= 3 {
  10.             break
  11.         }
  12.         time.Sleep(time.Second)
  13.     }
  14.     wg.Done()
  15. }
  16. func main() {
  17.     var wg sync.WaitGroup
  18.     for i := 0; i < 1000; i++ {
  19.         wg.Add(1)
  20.         go request(fmt.Sprintf("https://127.0.0.1:8080/%d", i), &wg)
  21.     }
  22.     wg.Wait()
  23. }

Q3 Go 可以限制运行时操作系统线程的数量吗?

答案:

The GOMAXPROCS variable limits the number of operating system threads that can execute user-level Go code simultaneously. There is no limit to the number of threads that can be blocked in system calls on behalf of Go code; those do not count against the GOMAXPROCS limit.

可以使用环境变量 GOMAXPROCS 或 runtime.GOMAXPROCS(num int) 设置,例如:

runtime.GOMAXPROCS(1// 限制同时执行Go代码的操作系统线程数为 1
  

从官方文档的解释可以看到,GOMAXPROCS 限制的是同时执行用户态 Go 代码的操作系统线程的数量,但是对于被系统调用阻塞的线程数量是没有限制的。GOMAXPROCS 的默认值等于 CPU 的逻辑核数,同一时间,一个核只能绑定一个线程,然后运行被调度的协程。因此对于 CPU 密集型的任务,若该值过大,例如设置为 CPU 逻辑核数的 2 倍,会增加线程切换的开销,降低性能。对于 I/O 密集型应用,适当地调大该值,可以提高 I/O 吞吐率。

往期精彩

Go 语言笔试面试题(实现原理)

Go 语言笔试面试题(基础语法)

腾讯PHP/GO工程师面试经历

字节跳动Go 语言面试会问哪些问题?

【精选】2022年全新GO工程师面试题

欢迎分享给需要的朋友,求一键三连:分享朋友圈、点赞、在看

335515069bb123ac19d4f421aa532c05.png

文章来源: blog.csdn.net,作者:lxw1844912514,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/lxw1844912514/article/details/124679264

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。