Go 编程:在通讯中共享内存
Andrew Gerrand
2010 年 7 月 13 日
传统的线程模型 (例如, 通常用 Java、 C++ 以及 Python 编写程序时) 要求程序员使用共享内存在线程之间进行通信。 通常情况下, 共享数据结构受到锁的保护, 线程将争夺这些锁来访问数据。 在某些情况下, 通过使用线程安全的数据结构 (例如 Python 的 Queue) 可以使操作变得更容易。
Go 的并发原语 —— 协程和信道 —— 提供了一种独特而不失优雅的方式来构造并发软件。 (这些概念有一个 有趣的历史 以 C. A. R. Hoare 的 通信顺序过程 开头。) Go 明确使用锁来调节对共享数据的访问, 因此鼓励使用信道在协程之间传递对数据的引用。 这种方式可确保在给定时间只有一个协程可以访问数据。 该概念可以在文档 高效的 Go 编程 里看到总结 (值得所有 Go 程序员一读):
不要通过共享内存来通信; 而应通过通信来共享内存.
考虑一个轮询 URLs 列表的程序。 在传统的线程环境中, 人们可能会像这样构造其数据:
type Resource struct {
url string
polling bool
lastPolled int64
}
type Resources struct {
data []*Resource
lock *sync.Mutex
}
然后, 一个 Poller 函数 (其中的许多函数会在单独的线程中运行) 看起来如下:
func Poller(res *Resources) {
for {
// 获取最近轮询最少的资源
// 并将其标记为已轮询
res.lock.Lock()
var r *Resource
for _, v := range res.data {
if v.polling {
continue
}
if r == nil || v.lastPolled < r.lastPolled {
r = v
}
}
if r != nil {
r.polling = true
}
res.lock.Unlock()
if r == nil {
continue
}
// 查询 URL
// 更新资源的轮询和最后轮询的时间
res.lock.Lock()
r.polling = false
r.lastPolled = time.Nanoseconds()
res.lock.Unlock()
}
}
这个函数大概需要写满一页, 并且需要更多详细的代码才能完成想要的功能。 甚至还不包括 URL 轮询逻辑 (虽然只有几行), 更不会优雅地处理资源池的耗尽。
让我们看下使用 Go 套路实现的相同功能。 在此示例中, 轮询器是一个函数, 该函数从输入通道接收要轮询的资源, 并在完成后将其发送到输出通道。
type Resource string
func Poller(in, out chan *Resource) {
for r := range in {
// 轮询 URL
// 将处理后的资源发送出去
out <- r
}
}
在上一个示例中需要费尽心思的逻辑在这个示例显然不存在, 并且我们的资源数据结构也不需要包含记录数据。 实际上, 剩下的就是重要的部分。 这应该对你了解这门简单语言的函数功能有所帮助。
上面的代码片段还有不少遗漏之处。 这里有一些使用 Go 套路完成想法的程序, 请参阅 通过通信来共享内存.
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
推荐文章: