| # Share Memory By Communicating |
| 13 Jul 2010 |
| Tags: concurrency, technical |
| Summary: A preview of the new Go codelab, Share Memory by Communicating. |
| OldURL: /share-memory-by-communicating |
| |
| Andrew Gerrand |
| |
| ## |
| |
| Traditional threading models (commonly used when writing Java, |
| C++, and Python programs, for example) require the programmer to communicate |
| between threads using shared memory. |
| Typically, shared data structures are protected by locks, |
| and threads will contend over those locks to access the data. |
| In some cases, this is made easier by the use of thread-safe data structures |
| such as Python's Queue. |
| |
| Go's concurrency primitives - goroutines and channels - provide an elegant |
| and distinct means of structuring concurrent software. |
| (These concepts have an [interesting history](https://swtch.com/~rsc/thread/) that begins with C. |
| A. R. Hoare's [Communicating Sequential Processes](http://www.usingcsp.com/).) |
| Instead of explicitly using locks to mediate access to shared data, |
| Go encourages the use of channels to pass references to data between goroutines. |
| This approach ensures that only one goroutine has access to the data at a given time. |
| The concept is summarized in the document [Effective Go](https://golang.org/doc/effective_go.html) |
| (a must-read for any Go programmer): |
| |
| _Do not communicate by sharing memory; instead, share memory by communicating._ |
| |
| Consider a program that polls a list of URLs. |
| In a traditional threading environment, one might structure its data like so: |
| |
| type Resource struct { |
| url string |
| polling bool |
| lastPolled int64 |
| } |
| |
| type Resources struct { |
| data []*Resource |
| lock *sync.Mutex |
| } |
| |
| And then a Poller function (many of which would run in separate threads) might look something like this: |
| |
| 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() |
| } |
| } |
| |
| This function is about a page long, and requires more detail to make it complete. |
| It doesn't even include the URL polling logic (which, |
| itself, would only be a few lines), nor will it gracefully handle exhausting |
| the pool of Resources. |
| |
| Let's take a look at the same functionality implemented using Go idiom. |
| In this example, Poller is a function that receives Resources to be polled |
| from an input channel, |
| and sends them to an output channel when they're done. |
| |
| type Resource string |
| |
| func Poller(in, out chan *Resource) { |
| for r := range in { |
| // poll the URL |
| |
| // send the processed Resource to out |
| out <- r |
| } |
| } |
| |
| The delicate logic from the previous example is conspicuously absent, |
| and our Resource data structure no longer contains bookkeeping data. |
| In fact, all that's left are the important parts. |
| This should give you an inkling as to the power of these simple language features. |
| |
| There are many omissions from the above code snippets. |
| For a walkthrough of a complete, idiomatic Go program that uses these ideas, |
| see the Codewalk [_Share Memory By Communicating_](https://golang.org/doc/codewalk/sharemem/). |