在GO中 通过 通信共享内存 | Go主题月

488 阅读1分钟

传统的线程模型(例如,通常在编写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…