diff options
Diffstat (limited to 'doc/articles/concurrency_patterns.html')
-rw-r--r-- | doc/articles/concurrency_patterns.html | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/doc/articles/concurrency_patterns.html b/doc/articles/concurrency_patterns.html new file mode 100644 index 000000000..63c8cd59e --- /dev/null +++ b/doc/articles/concurrency_patterns.html @@ -0,0 +1,79 @@ +<!--{ +"Title": "Go Concurrency Patterns: Timing out, moving on", +"Template": true +}--> + +<p> +Concurrent programming has its own idioms. A good example is timeouts. Although +Go's channels do not support them directly, they are easy to implement. Say we +want to receive from the channel <code>ch</code>, but want to wait at most one +second for the value to arrive. We would start by creating a signalling channel +and launching a goroutine that sleeps before sending on the channel: +</p> + +{{code "/doc/progs/timeout1.go" `/timeout :=/` `/STOP/`}} + +<p> +We can then use a <code>select</code> statement to receive from either +<code>ch</code> or <code>timeout</code>. If nothing arrives on <code>ch</code> +after one second, the timeout case is selected and the attempt to read from +<cde>ch</cde> is abandoned. +</p> + +{{code "/doc/progs/timeout1.go" `/select {/` `/STOP/`}} + +<p> +The <code>timeout</code> channel is buffered with space for 1 value, allowing +the timeout goroutine to send to the channel and then exit. The goroutine +doesn't know (or care) whether the value is received. This means the goroutine +won't hang around forever if the <code>ch</code> receive happens before the +timeout is reached. The <code>timeout</code> channel will eventually be +deallocated by the garbage collector. +</p> + +<p> +(In this example we used <code>time.Sleep</code> to demonstrate the mechanics +of goroutines and channels. In real programs you should use <code> +<a href="/pkg/time/#After">time.After</a></code>, a function that returns +a channel and sends on that channel after the specified duration.) +</p> + +<p> +Let's look at another variation of this pattern. In this example we have a +program that reads from multiple replicated databases simultaneously. The +program needs only one of the answers, and it should accept the answer that +arrives first. +</p> + +<p> +The function <code>Query</code> takes a slice of database connections and a +<code>query</code> string. It queries each of the databases in parallel and +returns the first response it receives: +</p> + +{{code "/doc/progs/timeout2.go" `/func Query/` `/STOP/`}} + +<p> +In this example, the closure does a non-blocking send, which it achieves by +using the send operation in <code>select</code> statement with a +<code>default</code> case. If the send cannot go through immediately the +default case will be selected. Making the send non-blocking guarantees that +none of the goroutines launched in the loop will hang around. However, if the +result arrives before the main function has made it to the receive, the send +could fail since no one is ready. +</p> + +<p> +This problem is a textbook of example of what is known as a +<a href="https://en.wikipedia.org/wiki/Race_condition">race condition</a>, but +the fix is trivial. We just make sure to buffer the channel <code>ch</code> (by +adding the buffer length as the second argument to <a href="/pkg/builtin/#make">make</a>), +guaranteeing that the first send has a place to put the value. This ensures the +send will always succeed, and the first value to arrive will be retrieved +regardless of the order of execution. +</p> + +<p> +These two examples demonstrate the simplicity with which Go can express complex +interactions between goroutines. +</p> |