mirror of
https://github.com/golang/go
synced 2024-11-13 18:20:32 -07:00
d353d43d36
Fixes #3178. R=golang-dev, iant CC=golang-dev https://golang.org/cl/6506064
80 lines
3.1 KiB
HTML
80 lines
3.1 KiB
HTML
<!--{
|
|
"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
|
|
<code>ch</code> 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 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>
|