V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
yuanyao
V2EX  ›  Go 编程语言

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

  •  5
     
  •   yuanyao · 3 天前 · 8951 次点击

    两个 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 基础了。

    第 1 条附言  ·  3 天前
    工作职责
    1. 参与后端系统搭建与架构设计,保证其可扩展性,稳定性;为业务快速迭代提供保障;
    3. 作为业务 owner ,负责准时和高质量的交付;
    4. 根据业务需要,对后续架构的设计作出规划。
    任职要求
    1. 本科及以上学历,计算机科学、软件工程、信息技术或相关专业。
    2. 扎实的编程基础和良好的编码习惯,热爱编程,有 Go 或 Java 开发经验。
    3. 对产品有较好的理解,能够以用户体验为核心驱动力进行开发。
    4. 具备一定的系统架构设计能力,熟悉后端开发技术:协议、架构、存储、缓存、安全、消息队列等。
    5. 具备良好的沟通能力和团队合作精神,积极乐观,认真负责。
    103 条回复    2025-02-27 17:12:37 +08:00
    1  2  
    NerdHND
        1
    NerdHND  
       3 天前   ❤️ 1
    拿我得指出哥们候选人真的有问题吧, 这, 这不是完全不会嘛...
    hingle
        2
    hingle  
       3 天前   ❤️ 3
    薪资给多少?薪资低那就 “门当户对” 了,挺好的。
    kandaakihito
        3
    kandaakihito  
       3 天前   ❤️ 12
    看了一下,上周那个 “一面出 LRU 算法题算难吗” 的帖子也是你发的。老哥你负责面的到底是什么类型岗位呀?怎么题目差距好像有点儿大。

    而且说实话,如果给你投简历的人里面,有 80%连 chan 的基础都用不清楚。。。那么你这个岗位面试的时候应该不适合问 LRU 。
    nomagick
        4
    nomagick  
       3 天前   ❤️ 2
    你也别有莫名的优越感然后老来收铜币;
    先用务实标准筛选一下吧,统招本科四六级,3 年经验之类的
    Keystroke
        5
    Keystroke  
       3 天前
    time.sleep……
    clemente
        6
    clemente  
       3 天前
    如果说从通过率分布的角度来定义 题的难度的话
    90% 应该算 hard
    fruitmonster
        7
    fruitmonster  
       3 天前
    虽然知道应该使用 waitgroup ,但是, "time sleep " 没解决没执行完就退出这个问题么?
    czfy
        8
    czfy  
       3 天前 via Android   ❤️ 29
    钓鱼上瘾是病,得治
    xuanbg
        9
    xuanbg  
       3 天前
    你这个不叫 go 基础,就是个编程基础。
    jworg
        10
    jworg  
       3 天前
    可以用第三方库不,我脑子已经被 conc 惯坏了,只记得起 conc 的 WaitGroup , 系统的 WaitGroup 好像属于 sync 包。
    langhuishan
        11
    langhuishan  
       3 天前   ❤️ 1
    golang 的精髓不就是并发吗?这都没掌握,等于没学
    cooooing
        12
    cooooing  
       3 天前 via Android
    @fruitmonster time sleep 只是降低出现这个问题的概率,并没有解决问题。如果操作是请求 api 或者其他耗时操作,要 sleep 多久?这种方式不好的,还是得 wait group 或者其他方式来同步
    Vegetable
        13
    Vegetable  
       3 天前
    这种情况至少说明完全没做过需要 graceful shutdown 的程序,不能说判死刑,但至少对 go 不太了解吧
    lasuar
        14
    lasuar  
       3 天前
    可以说说招聘岗位要求几年经验,给多少薪资
    zljklang
        15
    zljklang  
       3 天前
    package main

    import "fmt"

    func main() {
    // 创建无缓冲通道
    ch := make(chan int)

    // 发送方 goroutine
    go func() {
    for i := 0; i < 5; i++ {
    ch <- i // 顺序发送数字
    }
    close(ch) // 发送完成后关闭通道[1,7](@ref)
    }()

    // 接收方 goroutine
    for num := range ch {
    fmt.Println("Received:", num)
    }
    }
    这个对吗
    givenge
        16
    givenge  
       3 天前   ❤️ 2
    天天发这些,不会是想转自媒体吧
    opengps
        17
    opengps  
       3 天前   ❤️ 1
    对于你的状态来考他们不合格,那反过来他们提问题考验你你有信心通过吗?
    单个问题并不能说明人的整体能力
    hunterster
        18
    hunterster  
       3 天前
    package main

    import (
    "fmt"
    "sync"
    )

    func writeData(c chan int, i int) {
    c <- i
    }

    func readData(c chan int) {
    for i := 0; i < 5; i++ {
    fmt.Println("The data is:", <-c)
    }
    }

    func main() {
    var wg sync.WaitGroup
    wg.Add(5)

    var c = make(chan int)

    for k := 1; k <= 5; k++ {
    go func() {
    defer wg.Done()
    writeData(c, k)
    }()
    }

    go readData(c)

    wg.Wait()
    close(c)
    }
    codersdp1
        19
    codersdp1  
       3 天前
    这个确实属于 go 基础了,没掌握这些写并发功能肯定会出问题的。
    voidmnwzp
        20
    voidmnwzp  
       3 天前
    ```go
    func Test2(t *testing.T) {
    s := make(chan int)
    go func() {
    for i := range s {
    fmt.Println("recv:",i)
    }
    }()
    for i := 0; i < 5; i++ {
    s <- i
    }
    close(s)
    }
    ```
    不是这和 1+。。100 求和有啥区别 很难吗
    SingeeKing
        21
    SingeeKing  
       3 天前
    想当年我也问过这类题,真的大量候选人答不出来😮‍💨
    expy
        22
    expy  
       3 天前
    正常,卷是真的卷,但是水平差的人也大量存在。
    HappyAndSmile
        23
    HappyAndSmile  
       3 天前
    你要求什么工资?我甚至可以答得比你好很多
    phpcyy
        24
    phpcyy  
       3 天前
    @voidmnwzp 你这就写错了啊,不能保证读的 goroutine 结束
    bv
        25
    bv  
       3 天前
    @SingeeKing 真的假的?都几年经验?这种人招进去也是边学边做吧,甚至还会拖队友后腿。
    main1234
        26
    main1234  
       3 天前   ❤️ 1
    package main

    import (
    "fmt"
    "sync"
    )

    func main() {
    sw := sync.WaitGroup{}
    sw.Add(1)
    ch := make(chan int)
    go func() {
    for i := 0; i < 5; i++ {
    ch <- i
    }
    close(ch)
    }()
    go func() {
    defer sw.Done()
    for i := range ch {
    fmt.Println(i)
    }
    }()
    sw.Wait()
    fmt.Println("done")
    }
    voidmnwzp
        27
    voidmnwzp  
       3 天前
    @phpcyy range channel 的话 如果 channel 关闭了会退出的 你可以试试
    yuanyao
        28
    yuanyao  
    OP
       3 天前
    @lasuar 三年左右,薪资我不清楚,我自己 22 年三年经验进来的时候给了 32k*14 ,不知道现在多少了
    yiqiao
        29
    yiqiao  
       3 天前
    @zljklang 你这个方法如果主 goroutine 发送完后立刻退出接收方还在运行导致提前终止吧 ,你可以试着调大循环可以看出来
    还是要引入 WaitGroup
    Ayanokouji
        30
    Ayanokouji  
       3 天前
    @jworg #10 是这个库吗,看起来不维护了
    https://github.com/sourcegraph/conc
    Licsber
        31
    Licsber  
       3 天前
    笑死 最近写了个转发助手 和你这个题完全重叠
    写出来加双端调试 部署上线测试总共两天吧
    来看看我这水平能开多少(

    ```go
    // listener 省略
    func handleConnection(conn net.Conn) {
    s := &Session{
    Cmd: link_start,
    Remote: remote,
    ServerSideRemote: conn.RemoteAddr().String(),
    ClientID: clientID,
    }

    ctx, cancel := context.WithTimeout(context.Background(), connectionTimeout)
    sessionManager.Set(s.ServerSideRemote, conn)
    sessionManager.Bind(s.ServerSideRemote, cancel)

    send, _ := json.Marshal(s)
    token := mqttClient.Publish(controlTopic, defaultQOS, false, send)
    if token.Wait() && token.Error() != nil {
    slog.Error("Publish control packet failed:", "err", token.Error())
    sessionManager.Remove(s.ServerSideRemote)
    return
    }

    <-ctx.Done()
    switch ctx.Err() {
    case context.Canceled:
    slog.Info("Link confirmed successfully:", "serversideremote", s.ServerSideRemote)
    case context.DeadlineExceeded:
    slog.Warn("Confirmation timeout after 5s:", "serversideremote", s.ServerSideRemote)
    sessionManager.Remove(s.ServerSideRemote)
    }
    }

    func runServerForwarding(s *Session) {
    conn, ok := sessionManager.Get(s.ServerSideRemote)
    if !ok {
    slog.Error("Connection not found:", "connID", s.ServerSideRemote)
    return
    }
    defer conn.Close()

    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    topic := topicPrefix + s.ClientSideLocal
    token := mqttClient.Subscribe(topic, defaultQOS, func(c mqtt.Client, m mqtt.Message) {
    select {
    case <-ctx.Done():
    return
    default:
    slog.Debug("Recv client down:", "recv", m.Payload())
    recv, err := hex.DecodeString(string(m.Payload()))
    if err != nil {
    slog.Error("HEX decode error:", "err", err)
    cancel()
    return
    }

    _, err = conn.Write(recv)
    if err != nil {
    slog.Warn("TCP write error:", "err", err)
    cancel()
    }
    }
    })
    if token.Wait() && token.Error() != nil {
    slog.Error("Subscribe client down error:", "err", token.Error())
    return
    }

    buf := make([]byte, defaultBufSize)
    for {
    select {
    case <-ctx.Done():
    return
    default:
    conn.SetReadDeadline(time.Now().Add(readTimeout))
    n, err := conn.Read(buf)
    if err != nil {
    if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
    slog.Debug("TCP read timeout:", "connID", s.ServerSideRemote)
    continue
    }

    slog.Warn("TCP read error:", "err", err)
    return
    }

    send := hex.EncodeToString(buf[:n])
    slog.Debug("Send up:", "send", send)
    topic := topicPrefix + s.ServerSideRemote
    token := mqttClient.Publish(topic, defaultQOS, false, send)
    if token.Wait() && token.Error() != nil {
    slog.Error("Publish up error:", "err", token.Error())
    return
    }
    }
    }
    }
    ```
    yuanyao
        32
    yuanyao  
    OP
       3 天前 via iPhone
    @zljklang 要求是新开两个 goroutine ,我没说清楚。你这个也没问题
    yuanyao
        33
    yuanyao  
    OP
       3 天前 via iPhone
    @hunterster 为啥要五个协程,不增加复杂度吗
    MoYi123
        34
    MoYi123  
       3 天前
    我三年前写的困难版本 (多生产者多消费者关 chan) https://github.com/mmooyyii/mmooyyii/blob/master/docs/go/mpmc_channel.md
    yuanyao
        35
    yuanyao  
    OP
       3 天前 via iPhone
    @nomagick 非 982 和几个特定的 211 ,简历都过不了吧
    jworg
        36
    jworg  
       3 天前
    @Ayanokouji 是的,不过基础功能的封装库并不需要经常更新,看了眼作者也还活着。
    hanxiansheng
        37
    hanxiansheng  
       3 天前 via Android
    法拉利跑车连犁田都不会,呵呵
    phpcyy
        38
    phpcyy  
       3 天前
    @voidmnwzp 把 5 调大成 10000 ,多试几次,有概率不到 9999 就退出
    leehaoze98
        39
    leehaoze98  
       3 天前
    面外包或者校招会问两个协程交替打印,一个打印 A~Z ,一个打印 1~100 。

    1. 有的人手打从'A'~'Z'的数组
    2. 有的人 goroutine 没启动,主进程就退出了
    3. 有的人只会处理两个协程打印数量一致的情况,一个打印 26 个,另一个打印 100 个,好多人会死锁。
    phpcyy
        40
    phpcyy  
       3 天前
    @voidmnwzp 试了下,直接用 main 函数,不使用单元测试文件,即使是 5 也有概率到 3 就退出
    voidmnwzp
        41
    voidmnwzp  
       3 天前
    @phpcyy #40 func Test2(t *testing.T) {
    s := make(chan int)
    wg:=sync.WaitGroup{}
    wg.Add(1)
    go func() {
    for i := range s {
    fmt.Println("go2:",i)
    }
    fmt.Println("done")
    wg.Done()
    }()
    for i := 0; i < 5; i++ {
    s <- i
    }
    close(s)
    wg.Wait()
    }
    smallparking
        42
    smallparking  
       3 天前
    ```
    package main

    import (
    "fmt"
    "sync"
    )

    func send(ch1 chan int) {
    for i := range 5 {
    ch1 <- i
    fmt.Printf("send i: %d\n", i)
    }
    close(ch1)
    }
    func recv(wg *sync.WaitGroup, ch1 chan int) {

    // wait receive all data and done
    for i := range ch1 {
    fmt.Printf("receive i: %d\n", i)
    }
    wg.Done()

    }
    func main() {
    var wg sync.WaitGroup
    ch1 := make(chan int)
    wg.Add(1)
    go send(ch1)
    go recv(&wg, ch1)
    wg.Wait()
    }
    ```

    确实有点难度,一般不会启两个 goroutine, 所以确实不是一下子写出来的
    phpcyy
        43
    phpcyy  
       3 天前
    @voidmnwzp 这个没问题。楼上有例子,你把生产者放 goroutine 先,然后消费者放到 main 函数里,,是最简单的实现
    ChatGOP
        44
    ChatGOP  
       3 天前
    Gobyexample 大把例子。
    xiangxiangxiang
        45
    xiangxiangxiang  
       3 天前
    写了快两年 go 。。。确实基本没用过 channel ,只有看 b 站学 go 语言的时候用过

    WaitGroup 倒是工作中经常用
    k9982874
        46
    k9982874  
       3 天前
    这个不提前复习一下直接上手没 ai 辅助真写不出一次全对
    vhwwls
        47
    vhwwls  
       3 天前
    运维路过,这个我觉得应该是贵司的简历筛选出了问题,一楼说的对。
    daimazha
        48
    daimazha  
       3 天前
    我觉得这也是八股文的一种
    ChatGOP
        49
    ChatGOP  
       3 天前
    @daimazha 是的, 八股文。 而且哥觉得凡是八股文,AI/Google 能做的都不算是真正的能力
    Nazz
        50
    Nazz  
       3 天前
    我用 chan 从来不 close
    zhengfan2016
        51
    zhengfan2016  
       3 天前   ❤️ 10
    只能说 op 招聘错方向了,你招的 985 ,211 都是市场里不缺面试机会的那一批。如果不是特别热爱代码的,也不会去钻研这些东西,反正 curd 就能轻松把钱拿,下班了打游戏不香吗。

    倒是那些学历低人一等的大专和普本,有少部分人为了在竞争中脱颖而出,可能真会去钻研这种东西,而聪明的 op 已经把这种可能性过滤掉了。

    至此,面试者百分之 90%不会不是很正常吗
    hunterster
        52
    hunterster  
       3 天前
    @yuanyao 这个是看到要求随手写的,没注意到只要两个协程,我改一下

    package main

    import (
    "fmt"
    "sync"
    )

    var wg sync.WaitGroup

    func init() {
    wg.Add(5)
    }

    func writeData(c chan int, count int) {
    fmt.Println("Write rountine is executed.")
    for k := 0; k < count; k++ {
    defer wg.Done()
    c <- k
    }
    }

    func readData(c chan int, count int) {
    fmt.Println("Read rountine is executed.")
    for i := 0; i < count; i++ {
    fmt.Println("The data is:", <-c)
    }
    }

    func main() {

    var c = make(chan int)

    go writeData(c, 5)
    go readData(c, 5)

    wg.Wait()
    close(c)
    }
    Erroad
        53
    Erroad  
       3 天前
    @zhengfan2016 #50 我建议是他先给他们公司的搞个考试,不合格的都开了再说
    birdhk
        54
    birdhk  
       3 天前
    package main

    import "sync"

    func main() {
    var wg sync.WaitGroup
    ch := make(chan int)
    wg.Add(2)
    go func() {
    defer wg.Done()
    for i := 0; i < 10000; i++ {
    ch <- i
    }
    close(ch)

    }()
    go func() {
    defer wg.Done()
    for i := range ch {
    println(i)
    }
    }()
    wg.Wait()
    }
    我感觉我这样应该没问题,有大哥们 review 一下吗
    Co1a
        55
    Co1a  
       2 天前
    楼主发下邮箱
    ForkNMB
        56
    ForkNMB  
       2 天前
    刚开始学 go 也以为是八股文看看得了 不上手试试 写的也是简单版本(因为没有具体复杂的场景 写出来当然简单)等真用 go 开发写服务 多个协程下 服务优雅关闭 资源合理释放 生产者消费者怎么稳定的转起来 还是踩了很多坑的= = 面试这个确实能筛人
    sky3hao
        57
    sky3hao  
       2 天前
    这个还好, 我之前出的面试题是: 用两个 channel 循环打印水分子(H2O), 分子顺序不要求, 但是每一行必须是 2 个 H 加 1 个 O 的组合
    ChatGOP
        58
    ChatGOP  
       2 天前   ❤️ 2
    @ForkNMB 其实招人的, 如果自己没有看过这题没有准备, 拿这题考他自己, 也会被筛出局。
    body007
        59
    body007  
       2 天前
    @hunterster #52 看到循环里面用 defer ,jb 都报警告了。生产环境被同事这种代码坑过
    Jinnrry
        60
    Jinnrry  
       2 天前 via iPhone   ❤️ 2
    我面试也问这个题目。感觉和 hr 相关,很多 hr 只看背景,不懂技术,找一堆人过来就 90%都写不出来。

    我现在这里 hr 比较专业,基本上一半的人能写出来的。

    想来也是搞笑,这些连个协程通信都写不明白的人,讲起垃圾回收,数组扩容,乐观锁悲观锁却能头头是道
    guanhui07
        61
    guanhui07  
       2 天前
    也是八股文的一种 不过如果他能知道 waitGroup 我就让他过
    seakee
        62
    seakee  
       2 天前
    ```
    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
        63
    Trim21  
       2 天前
    这还八股啊,这不是 go 并发编程的基础知识吗 ...
    kalista
        64
    kalista  
       2 天前
    我有个问题,你们面试写代码允许用 cursor 吗
    prosgtsr
        65
    prosgtsr  
       2 天前
    多线程的题是挺难答的。如果不经常复习的话。一段时间就忘了。甚至就连 juc 自带的几个工具叫啥都忘了。。
    ericcen
        66
    ericcen  
       2 天前 via iPhone
    @body007 老哥,这个啥插件
    hxzhouh1
        67
    hxzhouh1  
       2 天前
    大佬,求个面试机会呀
    bitfly
        68
    bitfly  
       2 天前 via Android
    虽然我也不熟 但是真写过这玩意儿
    在网络上并发处理随机遍历数组且不重复就需要用到这玩意
    网络的不稳定性和结果返回的不确定性 的确很多 panic 修修补补不少时间配合 gpt 大法
    Go 的并发网络遍历还是挺无敌的
    harryge
        69
    harryge  
       2 天前
    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
        70
    voidmnwzp  
       2 天前
    @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
        71
    securityCoding  
       2 天前
    扔给 ai 就好了
    bbao
        72
    bbao  
       2 天前
    生产者消费者再业务开发中是非常常见的场景,考察代码能力挺好的,只是题目中有一个点会让人很奇怪,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
        73
    body007  
       2 天前
    @ericcen goland 自带功能哦,你也可以用那个代码检查的工具,也可以检查出循环里面的 defer
    Goooooos
        74
    Goooooos  
       2 天前 via Android
    @Trim21 我见过说问算法也说是八股,反正基础知识问了都是八股
    lixikei
        75
    lixikei  
       2 天前
    是不是这样子的
    ```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
        76
    zhangfuguan  
       2 天前
    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
        77
    CyJaySong  
       2 天前
    帮陪朋友发的
    ```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
        78
    guanzhangzhang  
       2 天前
    我们同事写 python 的,也只有 web 框架限制了他们写代码用错误 raise ,写非 web 场景全部是糊屎,从不用抛出错误,单独得加一个布尔返回值来上层接收函数内报错了没。基本没看到使用 class 而是写几百行 if else ,少数前人留下的 class 里又在__init 里加 os.exit 的
    zxjxzj9
        79
    zxjxzj9  
       2 天前
    这个确实还挺有水平的,因为一般人不写这种玩具小程序,用 goroutine 都是在 http 框架的处理器里写,确实不会在意这个退出的问题。 不过其实解决方法也很简单,就是每个 goroutine 开始前塞一个 wg.add(),运行完了塞一个 wg.done()就可以了
    Huelse
        80
    Huelse  
       2 天前
    这个水平要求没啥问题,但实际上你们最后还是会选薪资要的低的
    phenixc
        81
    phenixc  
       2 天前
    package main

    import (
    "fmt"
    "sync"
    )

    func senddata(out chan <- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := range 5 {
    out <- i
    }

    close(out)
    }

    func receivedata(in <- chan int, wg *sync.WaitGroup) {
    defer wg.Done()

    for i := range in {
    fmt.Println(i)
    }
    }

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

    wg.Add(2)
    go senddata(ch, &wg)
    go receivedata(ch, &wg)

    wg.Wait()
    }
    capgrey
        82
    capgrey  
       2 天前
    我比较好奇,为什么你懂,候选者不懂。是你们学习用的阅读材料的差异吗? op 看哪个文档的,Effective Go 吗?
    caicaiwoshishui
        83
    caicaiwoshishui  
       2 天前
    func main() {

    ch := make(chan int, 1)
    wg := sync.WaitGroup{}
    count := 5
    wg.Add(1) // Add 1 to the WaitGroup before starting the producer goroutine
    go func(wg *sync.WaitGroup, ch chan int, count int) {
    defer wg.Done()
    for i := 0; i <= count; i++ {
    ch <- i
    }
    }(&wg, ch, count)

    go func() {
    for {
    select {
    case v, ok := <-ch:
    if !ok { // Check if the channel is closed
    return
    }
    fmt.Println(v)
    }
    }
    }()

    wg.Wait()
    close(ch)
    fmt.Println("end")
    }
    sardina
        84
    sardina  
       2 天前   ❤️ 1
    https://go.dev/play/p/Te_KHn3sWYN 只用 channel 实现
    大哥,你们公司在哪个城市,我想去
    2018yuli
        85
    2018yuli  
       2 天前
    +1
    duty
        86
    duty  
       2 天前
    只能说感谢楼主,本来是一个 Go 开发,来到这家公司除了第一个项目后面全是写 python ,看到这个题的时候打开编辑器,基础语法都错了五六次,虽然最后实现了,但还是该反思自己把基础知识都整忘了,该重新抓起来了,也该开始计划换工作了,这样下去,感觉以后的路也会被糟蹋了
    hunterster
        87
    hunterster  
       2 天前
    @body007 我用的是 liteide 写的,没有这个错误提示
    mayli
        88
    mayli  
       2 天前
    正常吧,go 基础,没写过的很容易挂
    Rehtt
        89
    Rehtt  
       2 天前 via Android
    也就两个 goroutine ,生产者和消费者都只有一个 goroutine 根本不需要 waitgroup ,两个 channel 就可以了,一个负载消息一个用来退出阻塞就行了
    loginv2
        90
    loginv2  
       2 天前
    v2 是回复里面不支持 md 语法么 怎么这么多人都发的没缩进了
    UN2758
        91
    UN2758  
       2 天前
    @harryge 你这个 time.sleep 真是把我干沉默了,假如如果我是面试官,你没有主动解释这样思路不规范的写法的话,评分不会很好
    UN2758
        92
    UN2758  
       2 天前
    ```
    package main

    import (
    "fmt"
    "sync"
    )

    func producer(c chan int, wg *sync.WaitGroup) {
    for i := 0; i < 10000; i++ {

    c <- i
    }
    close(c)
    }

    func consumer(c chan int, wg *sync.WaitGroup) {
    for i := range c {
    fmt.Println("consumer", i)
    }
    wg.Done()
    }

    func main() {
    c := make(chan int)
    wg := sync.WaitGroup{}
    wg.Add(1)

    go producer(c, &wg)

    go consumer(c, &wg)
    wg.Wait()
    }
    ```
    buffzty
        93
    buffzty  
       2 天前
    用不着 waitgroup ,context ,done channel 这些啊 for 循环就行了,关闭了循环就退出了
    package main

    import "log"

    var (
    jobs = make(chan int)
    )

    func t2() {
    for v := range jobs {
    log.Println(v)
    }
    }
    func t1() {
    for i := 0; i < 5; i++ {
    jobs <- i
    }
    close(jobs)
    }
    func main() {
    go t1()
    t2()
    }
    angeni
        94
    angeni  
       2 天前
    平台决定的候选人素质
    lesismal
        95
    lesismal  
       1 天前   ❤️ 1
    > 2. 有的 channel 不知道由生产者关闭,直接在主程序生产者还未发送结束就关闭结果 panic ;

    这种面试题用一个 chan 可以,但但就这个面试题的功能的话似乎就没必要俩协程了,不需要用俩协程也就不需要用 chan 了。
    所以这种题如果出给我、只是纸面作答、我是不知道怎么答只能空着,因为需求不合理。

    很多基础场景生产者不是唯一的,可能会是并发多协程会生产,所以通常是应该把用于发送的和用于关闭的分开两个 chan 、用于 close 的 chan 再配个 atomic compareandswap ,避免用单个 chan 、某个地方关闭后、其他协程还在给 chan 发数据直接就 panic 了,一些粗暴的实现直接 recover 这种 panic 虽然也问题不大但毕竟它不是个好的处理方式、比如还得纠结 panic recover 是否再给调用者一个 ErrClosed 返回,还是两个 chan 好些。

    另外,如果不需要清理 chan 内遗留的数,chan 本身用完之后是不需要 close 的。
    gaifanking
        96
    gaifanking  
       1 天前
    类似生产者消费者、状态机这种比较典型的场景/思路都有哪些呢?
    sampeng
        97
    sampeng  
       1 天前 via iPhone
    说实话,我面试的时候也被问过这个类似的问题。没答上来,得到的评价是不会 golang 。这妨碍我能开发 golang 完整的项目吗?我当时回答的思路基本是自己撸一个 waitgroup ,因为当时一瞬间压根没想到 waitgroup 现成的。但我知道要有类似 waitgroup 的极致。实际开发不管是问 AI 还是 google 搜索,只要我思路有了,这也不费事。所以之后我在面试基本不会卡关键词,重点是有没思路。思路是不是自己想的和经验得来的…
    Steaven
        98
    Steaven  
       1 天前
    怎么跟我面试差不多,上周四下班后面试的
    Steaven
        99
    Steaven  
       1 天前
    两种方法,第一种:
    package main

    import (
    "fmt"
    "sync"
    )

    func main() {
    var wg sync.WaitGroup
    initial := make(chan struct{})
    ch := make(chan int)
    prev := initial
    for i := 1; i <= 100; i++ {
    wg.Add(1)
    next := make(chan struct{})
    go func(i int, prev, next chan struct{}) {
    <-prev
    ch <- i
    close(prev)
    if i < 100 {
    next <- struct{}{}
    }
    }(i, prev, next)
    prev = next
    }

    initial <- struct{}{}
    go func() {
    for i := range ch {
    wg.Done()
    fmt.Println(i)
    }
    }()

    wg.Wait()
    }

    第二种(使用锁):
    package main

    import (
    "fmt"
    "sync"
    )

    func main() {
    var wg sync.WaitGroup
    var mu sync.Mutex
    ch := make(chan int)
    cond := sync.NewCond(&mu)
    current := 1

    for i := 1; i <= 100; i++ {
    wg.Add(1)
    go func(num int) {
    mu.Lock()
    for current != num {
    cond.Wait()
    }
    ch <- i
    current++
    cond.Broadcast()
    mu.Unlock()
    }(i)
    }

    go func() {
    for i := range ch {
    wg.Done()
    fmt.Println(i)
    }
    }()

    wg.Wait()
    }
    tuxz
        100
    tuxz  
       1 天前
    ```go
    func main() {
    ch := make(chan int)
    doneCh := make(chan struct{})

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

    go func(ch <-chan int) {
    for v := range ch {
    fmt.Println(v)
    }
    close(doneCh)
    }(ch)

    <-doneCh
    }
    ```
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5086 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 09:16 · PVG 17:16 · LAX 01:16 · JFK 04:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.