23

那么除了处理多个服务器请求之外,还有其他时间与并发相关吗?我问是因为它内置在语言中,如果我不使用它,我会觉得很浪费,但我几乎找不到它的用途。

4

5 回答 5

10

(还)不是专家,Go但我会说:

无论何时最容易这样做。

并发模型的美妙之处Go在于,它从根本上不是一个多核架构,它在事情通常会中断的地方进行检查和平衡——它是一种多线程范式,不仅非常适合多核架构,而且也非常适合进入分布式系统架构。

您不必为多goroutines个人和谐地一起工作做出特殊安排——他们就是这样做的!

这是一个自然并发算法的示例 - 我想将多个通道合并为一个。一旦所有输入通道都用完,我想关闭输出通道。

使用并发更简单——实际上它甚至不像并发——它看起来几乎是程序化的。

/*
  Multiplex a number of channels into one.
*/
func Mux(channels []chan big.Int) chan big.Int {
    // Count down as each channel closes. When hits zero - close ch.
    var wg sync.WaitGroup
    wg.Add(len(channels))
    // The channel to output to.
    ch := make(chan big.Int, len(channels))

    // Make one go per channel.
    for _, c := range channels {
        go func(c <-chan big.Int) {
            // Pump it.
            for x := range c {
                ch <- x
            }
            // It closed.
            wg.Done()
        }(c)
    }
    // Close the channel when the pumping is finished.
    go func() {
        // Wait for everyone to be done.
        wg.Wait()
        // Close.
        close(ch)
    }()
    return ch
}

在这里我必须对并发做出的唯一让步是使用 async.WaitGroup作为并发计数的计数器。

请注意,这不仅仅是我自己的工作——我在这里得到了很多帮助。

于 2013-11-02T23:18:24.730 回答
7

这是 Go 的发明者之一 Rob Pike 使用并发的一个很好的例子,因为它是表达问题解决方案的一种更简单的方法:

Go 中的词法扫描

概括一下,任何生产者-消费者问题都非常适合使用通道将输出从生产者传递给消费者的 2 个 goroutine。

并发的另一个很好的用途是与多个输入/输出源(磁盘、网络、终端等)进行交互。每当结果来自任何这些来源时,您的程序应该能够唤醒并做一些工作。可以使用一个线程和一个系统调用(如 poll(2) 或 select(2))来执行此操作。当你的线程被唤醒时,它必须弄清楚是哪个结果进来的,找到它在相关任务中中断的地方,然后从那里开始。你需要写很多代码。

每个任务使用一个 goroutine 来编写代码要容易得多。然后该任务的状态在 goroutine 中被隐式捕获,并且从它停止的地方重新开始就像唤醒和运行一样简单。

于 2013-11-03T02:53:30.540 回答
4

我的 2 美分...如果您仅在并发上下文中考虑通道/goroutines,那么您就错过了这条船。

虽然 Go 不是一种对象语言,也不是严格意义上的函数式语言,但它确实允许您从两者中获取设计特性并应用它们。

面向对象设计的基本原则之一是单一职责 原则。应用此原则会迫使您根据消息而不是复杂的对象行为来考虑设计。这些相同的设计约束可以在 go 中使用,让您开始考虑连接单一用途功能的“通道上的消息”。

这只是一个例子,但如果你开始这样想,你会看到更多。

于 2013-11-18T17:27:23.467 回答
1

也不是围棋专家,所以我的一些方法可能是非规范的,但这里有一些我发现并发有用的方法:

  • 在等待网络请求、磁盘 I/O 或数据库查询完成时执行操作
  • 更快地执行分治算法
  • 由于 goroutine 是函数,而函数是 Go 中的一等公民,因此您可以将它们作为变量传递。当您的程序有许多自治部分时,这很方便。(例如,我正在模拟一个城市的交通系统。每辆车都是自己的 goroutine,它们通过通道与十字路口和其他车辆进行通信。每个人都做自己的事情。)
  • 跨不同设备的同时 I/O 操作
  • 使用并发对图像中的一组点执行 Dijkstra 算法以绘制“智能剪刀”线——每个点一个 goroutine 使这个实现显着加快。
  • GoConvey使用并发跨包同时运行测试,以便在使用Web UI进行调试时更快地响应更改。(作为一个固有的好处,这会为测试序列增加一些伪随机性,因此您的测试结果是真正一致的。)

当您的操作可以彼此独立运行但会按顺序运行时,并发可能(阅读:“有时,但不一定总是”)很有用。即使这些操作在某些点依赖于来自其他 goroutine 的数据或某种信号,您也可以通过通道进行通信。

有关这些问题的一些灵感和重要区别,以及一些有趣的 gopher 图片,请参阅Concurrency is not Parallelism

于 2013-11-03T23:10:52.017 回答
-2

任何时候一项数据不依赖于前一项。

于 2021-09-06T14:22:29.130 回答