【问题标题】:Getting len of large buffered channel blocks for loop为循环获取 len 个大型缓冲通道块
【发布时间】:2018-07-25 07:22:28
【问题描述】:

我遇到了一个奇怪的行为。我在玩缓冲通道,当使用大缓冲区时,整个程序执行会阻塞。在下面的代码sn-p中:

package main

import (
    "fmt"
)

func main() {
    choke := make(chan string, 150000)

    go func() {
        for i := 0; i < 10000000; i++ {
            choke <- string(i)
            fmt.Println("i=", i)
        }
    }()

    for {
        //fmt.Println(len(choke))
        if len(choke) >= 150000 {
            fmt.Println("Full")
        }
    }
}

我的程序在 ~96000 次迭代时阻塞并且永远不会达到“完整”打印,除非我在评估它之前打印出 len(choke)。这可能是由于fmt.Println 提供的延迟,因为这个问题也可以通过添加一个小的time.Sleep 来“修复”。

有人可以解释这种行为的原因吗?

【问题讨论】:

  • 你有一个紧密的循环,它将尽可能多地使用 CPU。获取通道的长度需要锁定通道,因此它会尽可能快地不断锁定和解锁它。您还在检查缓冲区大小是否为&gt;=,这意味着您认为它的长度有可能超过其最大大小,这在定义上是不可能的。
  • @Adrian 但问题是:为什么打印通道长度可以解决问题?
  • 很难说,你没有显示那个代码。
  • @Adrian 在if len(choke) &gt;= 150000 的正上方有一个fmt.Println(len(choke))。如果取消注释,程序将完美运行并且不会阻塞。不会用 print 锁定和解锁通道只会占用 CPU 更多,甚至更早地阻塞程序。
  • 这里有一个惯用的解决方案,可以在不阻塞 goroutine 的情况下确定通道是否已满:stackoverflow.com/questions/48939522/…

标签: go concurrency channels


【解决方案1】:

这是因为你的 goroutine 永远不会执行。你在 goroutine 之外的“for”循环是一个没有任何阻塞操作的紧密循环,这意味着它得到了所有的调度程序时间。因此,您创建的 goroutine 永远不会被安排执行。

当你有 print 语句时它会起作用,因为这是一个阻塞操作 (I/O),它会导致 Go 调度程序切换到你创建的 goroutine。

【讨论】:

  • 但是添加time.Sleep(1 * time.Millisecond)而不是打印语句也会使goroutine执行,这不是I/O操作。
  • 我在您回复的同时编辑了我的评论。我的意思是说“阻塞”,在这种情况下它阻塞的原因是因为 Print 语句是 I/O。
  • @DavidKomljenovic 不,这不是 I/O 操作,而是阻塞操作,这也允许 Go 运行时调度其他 goroutine。
猜你喜欢
  • 2018-06-24
  • 2014-01-30
  • 2014-05-26
  • 2019-08-14
  • 1970-01-01
  • 2021-09-01
  • 2012-08-10
  • 2015-10-12
  • 1970-01-01
相关资源
最近更新 更多