【问题标题】:Proper way to gain access to a channel length in Go在 Go 中访问通道长度的正确方法
【发布时间】:2017-09-14 20:42:51
【问题描述】:

我使用 Go 已经有一段时间了,而且每天都在进步,但我本身并不是专家。目前我正在处理并发和 goroutines,因为我认为这是我的 Go 工具带中的最后一个未知数。我想我已经掌握了它的窍门,但仍然绝对是初学者。

我遇到的问题对我来说似乎很基本,但我尝试过的任何工作都没有。我想找出一种计算通道长度的方法。

根据我收集到的信息,len() 仅适用于缓冲通道,因此在这种情况下对我没有帮助。我正在做的是分批从数据库中读取值。我有一个生成器函数,就像

func gen() chan Result {
  out := make(chan Result)

  go func() {
    ... query db
    for rows.Next() {
      out <- row
    }
     close(out)
   }()

  return out
}

那么我就这样使用它

c := gen()

...

// do other stuff

我想用输出通道返回计数,或者将所有计数包装在一个结构类型中并返回它。

像这样:

c, len := gen()

或:

a := gen()

fmt.Println(a.c)
fmt.Println(a.len)

我相信除了使用 atomic 之外,我已经尝试了所有方法,我认为这实际上会起作用,但我阅读了周围,显然使用 atomic 不是正确的事情。我还有哪些其他选项不会让我留下 0 或无限阻塞

谢谢!

【问题讨论】:

  • 你到底需要什么通道长度?
  • 因为我在做批次的时候,最后一批的长度不会和其他批次一样。所以我在计算批次后所做的事情将需要它。因此,如果我有 89 行并且我按 10 个批次选择,最后一个将只有 9
  • 您可以使用 sync/atomic 作为计数器,并在 for 循环的每次迭代中递增它,然后返回它。您的无缓冲通道没有“长度”;根据定义,它的长度始终为零。
  • 你没有缓冲通道,但即使你有,缓冲通道的长度在并发发送或接收时也没有用,因为一旦你阅读它。
  • @Gavin 公平点。必须是存储在结构中的原子,并且您必须有一种方法知道批处理何时完成,然后才能读取它以获得权威计数。无论如何,对于原始问题,无缓冲通道没有长度。

标签: go concurrency channel


【解决方案1】:

len 内置函数将返回通道的“长度”:

func len(v Type) int

len 内置函数根据类型返回 v 的长度:

Array: the number of elements in v.
Pointer to array: the number of elements in *v (even if v is nil).
Slice, or map: the number of elements in v; if v is nil, len(v) is zero.
String: the number of bytes in v.
Channel: the number of elements queued (unread) in the channel buffer;

如果 v 为零,则 len(v) 为零。

但我认为这对你没有帮助。

您真正需要的是一种解决问题的新方法:计算通道中队列中的项目并不是处理“批量”任务的合适方法。

你需要这个长度做什么?

【讨论】:

  • 一旦我从查询中获得了我的项目,我生成 goroutine 并对其进行处理。我把工作放在一个频道上,然后想覆盖它。我正在修改旧代码,所以它已经为工作人员使用了一个等待组,但现在它被新的单通道阻塞了。我能够将它切换到无缓冲并且它没有阻塞,但你需要一个 len 。所以长度将是批次的长度,不能保证在最后一次查询中长度相同。
  • 您能否在频道关闭前不阅读,然后数一数您获得了多少结果?
  • 这里是它的要点。 play.golang.org/p/kBTgohg2Ia 很遗憾,我无法向您展示我的工作代码。但是缓冲通道不会阻塞,但无缓冲会阻塞。这就是为什么我需要查询批次的 chan 长度。请注意,这一切都包含在一个 for 循环中,该循环在写入 done chan 时终止。当没有其他东西可以查询时,就会发生这种情况。我正在做批处理,因为我需要加密具有数百万行的表中的列
  • @ZeroHour 你的代码块,因为你不消费来自enc。创建另一个从中读取的 goroutine。
  • @zerkms 如果您查看 enc chan,我将其缓冲到与 gen chan 相同的长度。我选择 100 作为批量大小的示例。但正如我所说,最后一批的结果可能没有 LIMIT 数量那么多。
【解决方案2】:

您使用的是缓冲通道。谢谢你???? 无缓冲通道不使用内存。因此从不包含任何东西! 无缓冲通道的唯一目的是通过将元素从一个传递到另一个来实现 goroutine 之间的同步。而已 !

    go func(){
        c:=make(chan struct{})
        c<-struct{}{} // Definitely locked
    }()

又一个死锁

    go func(){
        c:=make(chan struct{})
        <-c // Definitely locked
        c<-struct{}{} // Never get there
    }()

使用另一个 goroutine 读取通道

    go func(){
        c:=make(chan struct{})
        go func(){<-c}()
        c<-struct{}{}
    }()

在您的情况下,您有一个生成器,这意味着您必须读取通道,直到生产者 goroutine 将其关闭。这是一个很好的设计,可以确保你的 goroutine 没有悬空。

// Read the channel until the injecter goroutine finishes and closes it.
for r := range gen() {
...
}
// goroutine inner of gen() as finished

【讨论】:

    【解决方案3】:

    我假设,根据您的后续回答,您实际上想知道工作人员池和通道上的缓冲区的“好”值,以保持一切“最佳”工作。

    这非常困难,并且取决于工人正在做什么,但首先我会在运行时查看缓冲通道和工人池的最小值。GOMAXPROCS(0)。如果你有很多资源,那么你可以去“无限”工人。

    【讨论】:

    • 实际上,我需要一个缓冲通道,它的长度与我生成的通道中的对象数量相同。出于某种我不太明白的原因,如果 wg.Wait() 没有缓冲,则在写入另一个通道时会阻止我。但是我能够通过为 wg.Wait() 生成另一个 goroutine 来解决这个问题,它现在可以使用无缓冲的通道。如果您有兴趣并有意见,这里是之前和之后。之前:play.golang.org/p/kBTgohg2Ia 之后:play.golang.org/p/kWB3nRC2U6
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    • 2015-02-25
    • 2012-04-17
    • 2019-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多