传统的线程模型(例如,通常在编写Java、c++和Python程序时使用)要求程序员使用共享内存在线程之间进行通信。通常,共享数据结构由锁保护,线程将争用这些锁来访问数据。在某些情况下,使用线程安全的数据结构(如Python的队列)可以简化这一点。
Go的并发原语——goroutines和channels——提供了一种优雅而独特的构建并发软件的方法。(这些概念有一段有趣的历史,始于 C. A. R. Hoare's Communicating Sequential Processes.。)Go鼓励使用通道在goroutines之间传递对数据的引用,而不是显式地使用锁来协调对共享数据的访问。这种方法确保在给定的时间只有一个goroutine可以访问数据。这一概念总结在《Effective Go》
不要通过共享内存进行通信;相反,通过通信来共享内存。
一个Pollerurl列表的程序。在传统的线程环境中,可能会这样构造数据:
type Resource struct {
url string
polling bool
lastPolled int64
}
type Resources struct {
data []*Resource
lock *sync.Mutex
}
然后,一个Poller函数(其中的许多函数将在单独的线程中运行)可能看起来像这样:
func Poller(res *Resources) {
for {
// get the least recently-polled Resource
// and mark it as being polled
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
}
// poll the URL
// update the Resource's polling and lastPolled
res.lock.Lock()
r.polling = false
r.lastPolled = time.Nanoseconds()
res.lock.Unlock()
}
}
此功能大约需要一页纸,并且需要更多详细信息才能完成。 它甚至不包括URL轮询逻辑(它本身只有几行),也不会优雅地处理资源池的耗尽。
让我们看一下使用Go惯用语实现的相同功能。 在此示例中,轮询器是一项功能,该函数从输入通道接收要轮询的资源,并在完成后将其发送到输出通道。 上述代码段有很多遗漏之处。 有关使用这些想法的完整,惯用的Go程序的演练,请参阅Codewalk的通过通信共享内存。
GOLANG网文翻译计划blog.golang.org/codelab-sha…