我在 golang 中错误地使用了频道吗?

Am I using channels incorrectly with golang?

我来自 Node.js 背景,在那里做一些异步工作非常容易,然后在 运行ning 任务完成后做一些更多的工作,我确信它在 Go 中也是如此,但我还没有完全理解通道的工作原理。

我正在为我玩的一个老游戏构建一个解析器,它分析拍卖数据日志中的行并解析它们以通过 socket io 流式传输到网站上的实时提要。一个文件一次可以发送 100 行,我的解析器必须一次分析每一行并从每一行中提取元信息(例如商品、商品价格等)

每一行都有这个 for 循环 运行 反对它(这假设项目列表的部分是从正则表达式派生的):

itemChannel := make(chan Item)

for _, itemName := range itemList {
    item := Item {
        Name: itemName,
    }

    // Long running method which does parsing for the item such as pricing, quantity and makes some http calls (runs 75ms on average)
    go item.FetchData(itemChannel)

    // Read from the channel when its done
    raw := <-itemChannel
    auction.Items = append(auction.Items, raw)
    auction.Seller = seller
}

auctions = append(auctions, auction)
fmt.Println("Appended auction: ", auction)
go c.publishToRelayService(auction)

现在(根据观察)似乎 raw := <-itemChannel 导致循环阻塞,直到 goroutine 完成并将其数据传回(这肯定意味着 运行ning as item.FetchData(itemChannel)会做同样的事情。当数据返回时,我如何从通道中读取数据,但尽可能快地跳出循环迭代。有些行中有 15-20 个项目,这会导致程序暂停 ~2-在解析下一行之前 3 秒。我希望能够比这更快地中断并处理下一行,以尽可能快地保持解析器。有没有类似于 Node 中的 Promises 的机制,我可以链接一个每次完成 item.FetchData()?

的完成处理程序

注意 fetchChannel 在所有获取工作完成后写入我的项目类型内部。

您可以编写一个不同的 go 例程来等待通道中的新数据并对其进行处理。 这样生产者和消费者是运行并行的,当生产者完成生产消费者时必须完成,因为这里消费者是一个轻量级进程

您可以使用 done 渠道来指示消费者已完成

这是您可以更改代码的方法

itemChannel := make(chan Item)
done := make(chan bool)
//Consumer
go func(channel chan Item) {
    for raw := range channel {
        auction.Items = append(auction.Items, raw)
        auction.Seller = seller
        auctions = append(auctions, auction)
    }
    done <- true
}(itemChannel)

//Producer
for _, itemName := range itemList {
    item := Item{
        Name: itemName,
    }

    // Long running method which does parsing for the item such as pricing, quantity and makes some http calls (runs 75ms on average)
    go item.FetchData(itemChannel)

}

<-done
fmt.Println("Appended auction: ", auction)
go c.publishToRelayService(auction)

就更广泛的问题而言,从 Node.js 渠道转移到 Go/CSP 渠道,您需要首先抛开回调的想法。我使用的每个 reactive/async 范例都是某种形式的回调函数,以便于使用。但是CSP并没有尝试这样。

Go 中不同的关键是 light-weight goroutines 的协同调度发生在广泛独立于操作系统线程的情况下(尽管实现者通常努力使它使用 CPU 核心尽可能通过引擎盖下的 OS 线程技巧)。与动作回调没有真正的比较。

每个goroutines都有自己独立的life-cycle。它可能很短。或者,如果一个 goroutine 包含一些循环,它可能会存在一段时间并且看起来更像一个演员(在演员模型中)。

这是探索通信顺序进程 (CSP) 所需的思维。沿着数字电子构建块的思路思考 goroutines 也是一个有用的类比。门和线类似于 goroutines 和通道。

此外,flip-flops 可以由多个门构建 - 同样,goroutine 可以由 'smaller' 个由内部通道连接的 goroutine 组成。如果你做对了,'bigger' goroutine 上的外部通道是它的协作者唯一关心的事情(内部通道是隐藏的)。

这开辟了设计软件的新途径,也是 Rob Pike 提倡的事情之一:Concurrency is not Parallelism。换个思路。

一个例子可能是模拟软件(更大范围的康威生命游戏)。基于对所涉及的每个细胞的个体行为进行建模,我看到了一个非常引人注目的血液在血管中流动和凝固的模拟。该演示有 4000 万个并发实体,这种方法的使用令人印象深刻,运行 在普通笔记本电脑上。