mirror of
https://github.com/golang/go
synced 2024-11-21 15:34:45 -07:00
doc: add Go Concurrency Patterns: Timing out, moving on article
Originally published on The Go Programming Language Blog, September 23, 2010. http://blog.golang.org/2010/09/go-concurrency-patterns-timing-out-and.html Update #2547. R=golang-dev, adg CC=golang-dev https://golang.org/cl/5815044
This commit is contained in:
parent
d724631a53
commit
5659826e43
@ -7,6 +7,8 @@ RAWHTML=\
|
||||
articles/error_handling.rawhtml\
|
||||
articles/slices_usage_and_internals.rawhtml\
|
||||
articles/laws_of_reflection.rawhtml\
|
||||
articles/c_go_cgo.rawhtml\
|
||||
articles/go_concurrency_patterns_timing_out_moving_on.rawhtml\
|
||||
articles/image_draw.rawhtml\
|
||||
effective_go.rawhtml\
|
||||
go1.rawhtml\
|
||||
@ -17,4 +19,4 @@ all: $(RAWHTML)
|
||||
godoc -url /doc/$< >$@
|
||||
|
||||
clean:
|
||||
rm -f $(RAWHTML)
|
||||
rm -f $(RAWHTML)
|
||||
|
@ -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>
|
@ -105,7 +105,7 @@ Guided tours of Go programs.
|
||||
<li><a href="http://blog.golang.org/2010/04/json-rpc-tale-of-interfaces.html">JSON-RPC: a tale of interfaces</a></li>
|
||||
<li><a href="/doc/articles/gos_declaration_syntax.html">Go's Declaration Syntax</a></li>
|
||||
<li><a href="/doc/articles/defer_panic_recover.html">Defer, Panic, and Recover</a></li>
|
||||
<li><a href="http://blog.golang.org/2010/09/go-concurrency-patterns-timing-out-and.html">Go Concurrency Patterns: Timing out, moving on</a></li>
|
||||
<li><a href="/doc/articles/go_concurrency_patterns_timing_out_moving_on.html">Go Concurrency Patterns: Timing out, moving on</a></li>
|
||||
<li><a href="/doc/articles/slices_usage_and_internals.html">Go Slices: usage and internals</a></li>
|
||||
<li><a href="http://blog.golang.org/2011/05/gif-decoder-exercise-in-go-interfaces.html">A GIF decoder: an exercise in Go interfaces</a></li>
|
||||
<li><a href="/doc/articles/error_handling.html">Error Handling and Go</a></li>
|
||||
|
@ -41,7 +41,12 @@ if [ "$goos" == "freebsd" ]; then
|
||||
c_go_cgo="cgo3 cgo4"
|
||||
fi
|
||||
|
||||
all=$(echo $defer_panic_recover $effective_go $error_handling $law_of_reflection $c_go_cgo slices go1)
|
||||
timeout="
|
||||
timeout1
|
||||
timeout2
|
||||
"
|
||||
|
||||
all=$(echo $defer_panic_recover $effective_go $error_handling $law_of_reflection $c_go_cgo $timeout slices go1)
|
||||
|
||||
for i in $all; do
|
||||
go build $i.go
|
||||
|
28
doc/progs/timeout1.go
Normal file
28
doc/progs/timeout1.go
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
package timeout
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func Timeout() {
|
||||
ch := make(chan bool, 1)
|
||||
timeout := make(chan bool, 1)
|
||||
go func() {
|
||||
time.Sleep(1e9) // one second
|
||||
timeout <- true
|
||||
}()
|
||||
|
||||
// STOP OMIT
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
// a read from ch has occurred
|
||||
case <-timeout:
|
||||
// the read from ch has timed out
|
||||
}
|
||||
|
||||
// STOP OMIT
|
||||
}
|
27
doc/progs/timeout2.go
Normal file
27
doc/progs/timeout2.go
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
package query
|
||||
|
||||
type Conn string
|
||||
|
||||
func (c Conn) DoQuery(query string) Result {
|
||||
return Result("result")
|
||||
}
|
||||
|
||||
type Result string
|
||||
|
||||
func Query(conns []Conn, query string) Result {
|
||||
ch := make(chan Result, 1)
|
||||
for _, conn := range conns {
|
||||
go func(c Conn) {
|
||||
select {
|
||||
case ch <- c.DoQuery(query):
|
||||
default:
|
||||
}
|
||||
}(conn)
|
||||
}
|
||||
return <-ch
|
||||
}
|
||||
|
||||
// STOP OMIT
|
Loading…
Reference in New Issue
Block a user