那么除了处理多个服务器请求之外,还有其他时间与并发相关吗?我问是因为它内置在语言中,如果我不使用它,我会觉得很浪费,但我几乎找不到它的用途。
5 回答
(还)不是专家,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
作为并发计数的计数器。
请注意,这不仅仅是我自己的工作——我在这里得到了很多帮助。
这是 Go 的发明者之一 Rob Pike 使用并发的一个很好的例子,因为它是表达问题解决方案的一种更简单的方法:
概括一下,任何生产者-消费者问题都非常适合使用通道将输出从生产者传递给消费者的 2 个 goroutine。
并发的另一个很好的用途是与多个输入/输出源(磁盘、网络、终端等)进行交互。每当结果来自任何这些来源时,您的程序应该能够唤醒并做一些工作。可以使用一个线程和一个系统调用(如 poll(2) 或 select(2))来执行此操作。当你的线程被唤醒时,它必须弄清楚是哪个结果进来的,找到它在相关任务中中断的地方,然后从那里开始。你需要写很多代码。
每个任务使用一个 goroutine 来编写代码要容易得多。然后该任务的状态在 goroutine 中被隐式捕获,并且从它停止的地方重新开始就像唤醒和运行一样简单。
我的 2 美分...如果您仅在并发上下文中考虑通道/goroutines,那么您就错过了这条船。
虽然 Go 不是一种对象语言,也不是严格意义上的函数式语言,但它确实允许您从两者中获取设计特性并应用它们。
面向对象设计的基本原则之一是单一职责 原则。应用此原则会迫使您根据消息而不是复杂的对象行为来考虑设计。这些相同的设计约束可以在 go 中使用,让您开始考虑连接单一用途功能的“通道上的消息”。
这只是一个例子,但如果你开始这样想,你会看到更多。
也不是围棋专家,所以我的一些方法可能是非规范的,但这里有一些我发现并发有用的方法:
- 在等待网络请求、磁盘 I/O 或数据库查询完成时执行操作
- 更快地执行分治算法
- 由于 goroutine 是函数,而函数是 Go 中的一等公民,因此您可以将它们作为变量传递。当您的程序有许多自治部分时,这很方便。(例如,我正在模拟一个城市的交通系统。每辆车都是自己的 goroutine,它们通过通道与十字路口和其他车辆进行通信。每个人都做自己的事情。)
- 跨不同设备的同时 I/O 操作
- 使用并发对图像中的一组点执行 Dijkstra 算法以绘制“智能剪刀”线——每个点一个 goroutine 使这个实现显着加快。
- GoConvey使用并发跨包同时运行测试,以便在使用Web UI进行调试时更快地响应更改。(作为一个固有的好处,这会为测试序列增加一些伪随机性,因此您的测试结果是真正一致的。)
当您的操作可以彼此独立运行但会按顺序运行时,并发可能(阅读:“有时,但不一定总是”)很有用。即使这些操作在某些点依赖于来自其他 goroutine 的数据或某种信号,您也可以通过通道进行通信。
有关这些问题的一些灵感和重要区别,以及一些有趣的 gopher 图片,请参阅Concurrency is not Parallelism。
任何时候一项数据不依赖于前一项。