最基础的 go 并发编程题,难倒了 90%的候选人

207 天前
 yuanyao

两个 goroutine 用 channel 通信,一个 goroutine 顺序发送 0,1,2,3,4 个数字,另一个 goroutine 接收并输出。 考察了 goroutine 的控制、channel 的关闭等基础知识,面试者写的代码各种问题。

  1. 有的 goroutine 还没启动程序就退出了,提示后仍想不到使用 waitgroup ,context ,done channel 等手段,而是用 time sleep 等待;
  2. 有的 channel 不知道由生产者关闭,直接在主程序生产者还未发送结束就关闭结果 panic ;
  3. 有的不会检查消费者读关闭 channel 的返回值,程序直接死循环死锁。

上周面试 5 个人只有 1 个人一次写出了执行没问题的代码,有 1 个经过提示也没写出来,剩下的能提示后逐步修改出正确的代码。

这个题还是很经典的,不用问 GMP 、垃圾回收算法等八股文,这个题就能看出 go 基础了。

11995 次点击
所在节点    Go 编程语言
108 条回复
guanhui07
207 天前
也是八股文的一种 不过如果他能知道 waitGroup 我就让他过
seakee
207 天前
```
func main() {
num := make(chan int, 5)
done := make(chan struct{})

go func() {
for n := range num {
fmt.Println(n)
}
close(done)
}()

go func() {
for i := 0; i < 5; i++ {
num <- i
}
close(num)
}()

<-done
}
```
Trim21
207 天前
这还八股啊,这不是 go 并发编程的基础知识吗 ...
kalista
207 天前
我有个问题,你们面试写代码允许用 cursor 吗
prosgtsr
207 天前
多线程的题是挺难答的。如果不经常复习的话。一段时间就忘了。甚至就连 juc 自带的几个工具叫啥都忘了。。
ericcen
207 天前
@body007 老哥,这个啥插件
hxzhouh1
207 天前
大佬,求个面试机会呀
bitfly
207 天前
虽然我也不熟 但是真写过这玩意儿
在网络上并发处理随机遍历数组且不重复就需要用到这玩意
网络的不稳定性和结果返回的不确定性 的确很多 panic 修修补补不少时间配合 gpt 大法
Go 的并发网络遍历还是挺无敌的
harryge
207 天前
package main
import (
"time"
"fmt"
)

func main() {
ch := make(chan int)
go sender(ch, 5)
go receiver(ch)
time.Sleep(1 * time.Second)
fmt.Println("done")
}

func receiver(ch chan int) {
for {
i, ok := <- ch
if !ok {
fmt.Println("Chan closed")
break
}
fmt.Println("received ", i)
}
}

func sender(ch chan int, n int) {
for i := 0; i < n; i++ {
fmt.Println("sent ", i)
ch <- i
}
close(ch)
}
voidmnwzp
207 天前
@leehaoze98 func Test2(t *testing.T) {
s := make(chan int)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
var i int
for {
if i>=90{
break
}
i=<-s
if i>=90{
break
}
i++
fmt.Printf("go1: %c\n",i)
s<-i
}
fmt.Println("done")
wg.Done()
}()

i := 65
fmt.Printf("main: %c\n",i)
s <- i
for {
if i>=90{
break
}
i = <-s
if i >= 90 {
break
}
i++
fmt.Printf("main: %c\n",i)
s <- i
}
wg.Wait()
close(s)
}
securityCoding
207 天前
扔给 ai 就好了
bbao
207 天前
生产者消费者再业务开发中是非常常见的场景,考察代码能力挺好的,只是题目中有一个点会让人很奇怪,waitgroup 通常用于多个 goroutine 并发执行且等待所有 goroutine 返回结果使用。比如同时请求 HTTP 、GRPC 等请求下由多个业务接口。

[两个 goroutine 用 channel 通信]

channel 原则由生产者来控制关闭。消费者直接消费,通常 v,ok:=chan 来做个逻辑判断是否关闭。
channel 另一个特点是对于已关闭的 channel 进行读时会默认返回 0 ,由第二个返回变量来判断是否关闭。所以通常消费者无需关心做额外的业务处理。

此题如果等待两个 goroutine 都结束,输出预期结果,务必需要一定的等待时间,原因在于生产者如果关闭了 channel ,则 channel 不会再有阻塞的能力。
例如:
c := make(chan int, 5)
go func{xxxxxx; close(c) }
go func(xxxxxx;}
<-c (此处不会进行任何阻塞,所以启动程序即服务进行运行结束。)

waitgroup 可以,sleep 可以,context.WithTimeout 也可以。只是这三个用在这个场景中,在真实的业务里会很奇怪。一般业务也很难遇见这样的场景吧。

楼主想根据一个场景设计题同时考察 channel 、waitgroup 或 context.Withtimeout 用法之外。还是要贴合一下实际场景好一些。

比如特性特点,使用场景能很熟悉即可。是不是要用 sleep 、context.Withtimeout 、waitgroup 并不是那么重要。原因在业务代码中,如果这样出现这样的代码逻辑,是在 codreview 时打回的。
body007
207 天前
@ericcen goland 自带功能哦,你也可以用那个代码检查的工具,也可以检查出循环里面的 defer
Goooooos
207 天前
@Trim21 我见过说问算法也说是八股,反正基础知识问了都是八股
lixikei
207 天前
是不是这样子的
```go
package main

import (
"fmt"
"sync"
)

func main() {
wg := sync.WaitGroup{}
ch := make(chan int)

wg.Add(2)
go func() {
defer wg.Done()
defer close(ch)

for i := 0; i < 5; i++ {
ch <- i
}

}()

go func() {
defer wg.Done()
for v := range ch {
fmt.Printf("i: %d\n", v)
}
}()

wg.Wait()
}


```
zhangfuguan
207 天前
package main

import (
"fmt"
"sync"
)

var wg sync.WaitGroup

func main() {
wg.Add(2)

ch := make(chan int32, 1)
go send(ch)
go read(ch)

wg.Wait()

fmt.Println("done")

}

func read(ch <-chan int32) {
for {
select {
case msg := <-ch:
fmt.Println(msg)
if msg == 10 {
wg.Done()
}
}
}
}

func send(ch chan<- int32) {
for i := int32(1); i <= 10; i++ {
ch <- i
}
close(ch)
wg.Done()
}




这样?
CyJaySong
206 天前
帮陪朋友发的
```go
package main

import (
"fmt"
)

func main() {
ch := make(chan int)
down := make(chan struct{})
go func() {
for i := 0; i < 5; i++ {
i := i
ch <- i
}
close(ch)
}()

go func() {
for num := range ch {
fmt.Println("Received:", num)
}
down <- struct{}{}
}()
<-down
}

```
guanzhangzhang
206 天前
我们同事写 python 的,也只有 web 框架限制了他们写代码用错误 raise ,写非 web 场景全部是糊屎,从不用抛出错误,单独得加一个布尔返回值来上层接收函数内报错了没。基本没看到使用 class 而是写几百行 if else ,少数前人留下的 class 里又在__init 里加 os.exit 的
zxjxzj9
206 天前
这个确实还挺有水平的,因为一般人不写这种玩具小程序,用 goroutine 都是在 http 框架的处理器里写,确实不会在意这个退出的问题。 不过其实解决方法也很简单,就是每个 goroutine 开始前塞一个 wg.add(),运行完了塞一个 wg.done()就可以了
Huelse
206 天前
这个水平要求没啥问题,但实际上你们最后还是会选薪资要的低的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1113786

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX