mirror of
https://github.com/golang/go
synced 2024-11-21 18:14:42 -07:00
parent
63e668d2ad
commit
bb3e3090c6
@ -837,7 +837,7 @@ new instance each time it is evaluated.
|
||||
|
||||
<pre>
|
||||
func NewFile(fd int, name string) *File {
|
||||
if file < 0 {
|
||||
if fd < 0 {
|
||||
return nil
|
||||
}
|
||||
f := File{fd, name, nil, 0};
|
||||
@ -1335,7 +1335,7 @@ In Go, enumerated constants are created using the <code>iota</code>
|
||||
enumerator. Since <code>iota</code> can be part of an expression and
|
||||
expressions can be implicitly repeated, it is easy to build intricate
|
||||
sets of values.
|
||||
<p>
|
||||
</p>
|
||||
<pre>
|
||||
type ByteSize float64
|
||||
const (
|
||||
@ -1962,6 +1962,10 @@ is never used.
|
||||
|
||||
<h3 id="sharing">Share by communicating</h3>
|
||||
|
||||
<p>
|
||||
Concurrent programming is a large topic and there is space only for some
|
||||
Go-specific highlights here.
|
||||
</p>
|
||||
<p>
|
||||
Concurrent programming in many environments is made difficult by the
|
||||
subtleties required to implement correct access to shared variables. Go encourages
|
||||
@ -1986,16 +1990,248 @@ Another way to think about this model is to consider a typical single-threaded
|
||||
program running on one CPU. It has no need for synchronization primitives.
|
||||
Now run another such instance; it too needs no synchronization. Now let those
|
||||
two communicate; if the communication is the synchronizer, there's still no need
|
||||
for other synchronization. Consider Unix pipelines: they fit this model just
|
||||
fine. Although Go's approach to concurrency originates in Hoare's
|
||||
for other synchronization. Consider Unix pipelines: they fit this model
|
||||
perfectly. Although Go's approach to concurrency originates in Hoare's
|
||||
Communicating Sequential Processes (CSP),
|
||||
it can also be seen as a type-safe generalization of Unix pipes.
|
||||
</p>
|
||||
|
||||
<h3 id="goroutines">Goroutines</h3>
|
||||
|
||||
<p>
|
||||
They're called <em>goroutines</em> because the existing
|
||||
terms—threads, coroutines, processes, and so on—convey
|
||||
inaccurate connotations. A goroutine has a simple model: it is a
|
||||
function executing in parallel with other goroutines in the same
|
||||
address space. It is lightweight, costing little more than the
|
||||
allocation of stack space.
|
||||
And the stacks start small, so they are cheap, and grow
|
||||
by allocating (and freeing) heap storage as required.
|
||||
</p>
|
||||
<p>
|
||||
Goroutines are multiplexed onto multiple OS threads so if one should
|
||||
block, such as while waiting for I/O, others continue to run. Their
|
||||
design hides many of the complexities of thread creation and
|
||||
management.
|
||||
</p>
|
||||
<p>
|
||||
Prefix a function or method call with the <code>go</code>
|
||||
keyword to run the call in a new goroutine.
|
||||
When the call completes, the goroutine
|
||||
exits, silently. (The effect is similar to the Unix shell's
|
||||
<code>&</code> notation for running a command in the
|
||||
background.)
|
||||
</p>
|
||||
<pre>
|
||||
go list.Sort(); // run list.Sort in parallel; don't wait for it.
|
||||
</pre>
|
||||
<p>
|
||||
A function literal can be handy in a goroutine invocation.
|
||||
<pre>
|
||||
func Announce(message string, delay int64) {
|
||||
go func() {
|
||||
time.Sleep(delay);
|
||||
fmt.Println(message);
|
||||
}() // Note the parentheses - must call the function.
|
||||
}
|
||||
</pre>
|
||||
<p>
|
||||
In Go function literals are closures: the implementation makes
|
||||
sure the variables referred to by the function survive as long as they are active.
|
||||
<p>
|
||||
These examples aren't too practical because the functions have no way of signaling
|
||||
completion. For that, we need channels.
|
||||
</p>
|
||||
|
||||
<h3 id="channels">Channels</h3>
|
||||
|
||||
<p>
|
||||
Like maps, channels are a reference type and are allocated with <code>make</code>.
|
||||
If an optional integer parameter is provided, it sets the buffer size for the channel.
|
||||
The default is zero, for an unbuffered or synchronous channel.
|
||||
</p>
|
||||
<pre>
|
||||
ci := make(chan int); // unbuffered channel of integers
|
||||
cj := make(chan int, 0); // unbuffered channel of integers
|
||||
cs := make(chan *os.File, 100); // buffered channel of pointers to Files
|
||||
</pre>
|
||||
<p>
|
||||
Channels combine communication—the exchange of a value—with
|
||||
synchronization—guaranteeing that two calculations (goroutines) are in
|
||||
a known state.
|
||||
</p>
|
||||
<p>
|
||||
There are lots of nice idioms using channels. Here's one to get us started.
|
||||
In the previous section we launched a sort in the background. A channel
|
||||
can allow the launching goroutine to wait for the sort to complete.
|
||||
</p>
|
||||
<pre>
|
||||
c := make(chan int); // Allocate a channel.
|
||||
// Start the sort in a goroutine; when it completes, signal on the channel.
|
||||
go func() {
|
||||
list.Sort();
|
||||
c <- 1; // Send a signal; value does not matter.
|
||||
}();
|
||||
doSomethingForAWhile();
|
||||
<-c; // Wait for sort to finish; discard sent value.
|
||||
</pre>
|
||||
<p>
|
||||
Receivers always block until there is data to receive.
|
||||
If the channel is unbuffered, the sender blocks until the receiver has
|
||||
received the value.
|
||||
If the channel has a buffer, the sender blocks only until the
|
||||
value has been copied to the buffer.
|
||||
</p>
|
||||
<p>
|
||||
A buffered channel can be used like a semaphore, for instance to
|
||||
limit throughput. In this example, incoming requests are passed
|
||||
to <code>handle</code>, which sends a value into the channel, processes
|
||||
the request, and then receives a value out of the channel.
|
||||
The capacity of the channel buffer limits the number of
|
||||
simultaneous calls to <code>process</code>.
|
||||
</p>
|
||||
<pre>
|
||||
var sem = make(chan int, MaxOutstanding)
|
||||
|
||||
func handle(r *Request) {
|
||||
sem <- 1; // Wait for active queue to drain.
|
||||
process(r); // May take a long time.
|
||||
<-sem; // Done; enable next request to run.
|
||||
}
|
||||
|
||||
func Serve(queue chan *Request) {
|
||||
for {
|
||||
req := <-queue;
|
||||
go handle(req); // Don't wait for handle to finish.
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
<p>
|
||||
Here's the same idea implemented by starting a fixed
|
||||
number of <code>handle</code> goroutines all reading from the request
|
||||
channel.
|
||||
The number of goroutines limits the number of simultaneous
|
||||
calls to <code>process</code>.
|
||||
This <code>Serve</code> function also accepts a channel on which
|
||||
it will be told to exit; after launching the goroutines it blocks
|
||||
receiving from that channel.
|
||||
</p>
|
||||
<pre>
|
||||
func handle(queue chan *Request) {
|
||||
for r := range queue {
|
||||
process(r);
|
||||
}
|
||||
}
|
||||
|
||||
func Serve(clientRequests chan *clientRequests, quit chan bool) {
|
||||
// Start handlers
|
||||
for i := 0; i < MaxOutstanding; i++ {
|
||||
go handle(clientRequests)
|
||||
}
|
||||
<-quit; // Wait to be told to exit.
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h3 id="chan_of_chan">Channels of channels</h3>
|
||||
<p>
|
||||
One of the most important properties of Go is that
|
||||
a channel is a first-class value that can be allocated and passed
|
||||
around like any other. A common use of this property is
|
||||
to implement safe, parallel demultiplexing.
|
||||
<p>
|
||||
In the example in the previous section, <code>handle</code> was
|
||||
an idealized handler for a request but we didn't define the
|
||||
type it was handling. If that type includes a channel on which
|
||||
to reply, each client can provide its own path for the answer.
|
||||
Here's a schematic definition of type <code>Request</code>.
|
||||
</p>
|
||||
<pre>
|
||||
type Request struct {
|
||||
args []int;
|
||||
f func([]int) int;
|
||||
resultChan <-chan int;
|
||||
}
|
||||
</pre>
|
||||
<p>
|
||||
The client provides a function and its arguments, as well as
|
||||
a channel inside the request object on which to receive the answer.
|
||||
</p>
|
||||
<pre>
|
||||
func sum(a []int) (s int) {
|
||||
for _, v := range a {
|
||||
s += v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
request := &Request{[]int{3, 4, 5}, sum, make(chan int)}
|
||||
// Send request
|
||||
client Requests <- request;
|
||||
// Wait for response.
|
||||
fmt.Printf("answer: %d\n", <-request.resultChan);
|
||||
</pre>
|
||||
<p>
|
||||
On the server side, the handler function is the only thing that changes.
|
||||
</p>
|
||||
<pre>
|
||||
func handle(queue chan *Request) {
|
||||
for req := range queue {
|
||||
req.resultChan <- req.f(req.args);
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
<p>
|
||||
There's clearly a lot more to do to make it realistic, but this
|
||||
code is a framework for a rate-limited, parallel, non-blocking RPC
|
||||
system, and there's not a mutex in sight.
|
||||
</p>
|
||||
|
||||
<h3 id="parallel">Parallelization</h3>
|
||||
<p>
|
||||
Another application of these ideas is to parallelize a calculation
|
||||
across multiple CPU cores. If the calculation can be broken into
|
||||
separate pieces, it can be parallelized, with a channel to signal
|
||||
when each piece completes.
|
||||
</p>
|
||||
<p>
|
||||
Let's say we have an expensive operation to perform on an array of items,
|
||||
and that the value of the operation on each item is independent,
|
||||
as in this idealized example.
|
||||
</p>
|
||||
<pre>
|
||||
type Vec []float64
|
||||
|
||||
// Apply the operation to n elements of v starting at i.
|
||||
func (v Vec) DoSome(i, n int, u Vec, c chan int) {
|
||||
for ; i < n; i++ {
|
||||
v[i] += u.Op(v[i])
|
||||
}
|
||||
c <- 1; // signal that this piece is done
|
||||
}
|
||||
</pre>
|
||||
<p>
|
||||
We launch the pieces independently in a loop, one per CPU.
|
||||
They can complete in any order but it doesn't matter; we just
|
||||
count the completion signals by draining the channel after
|
||||
launching all the goroutines.
|
||||
</p>
|
||||
<pre>
|
||||
const NCPU = 4 // number of CPU cores
|
||||
|
||||
func (v Vec) DoAll(u Vec) {
|
||||
c := make(chan int, NCPU); // Buffering optional but sensible.
|
||||
for i := 0; i < NCPU; i++ {
|
||||
go v.DoSome(i*len(v)/NCPU, (i+1)*len(v)/NCPU, u, c);
|
||||
}
|
||||
// Drain the channel.
|
||||
for i := 0; i < NCPU; i++ {
|
||||
<-c // wait for one task to complete
|
||||
}
|
||||
// All done.
|
||||
}
|
||||
|
||||
</pre>
|
||||
|
||||
<h3 id="leaky_buffer">A leaky buffer</h3>
|
||||
|
||||
<p>
|
||||
|
Loading…
Reference in New Issue
Block a user