mirror of
https://github.com/golang/go
synced 2024-11-22 00:14:42 -07:00
section on service multiplexing
R=gri DELTA=75 (57 added, 4 deleted, 14 changed) OCL=15394 CL=15398
This commit is contained in:
parent
1841f3114e
commit
6820196b75
@ -51,7 +51,10 @@ program that doesn't depend on "print()":
|
||||
--PROG progs/helloworld2.go
|
||||
|
||||
This version imports the ''os'' package to acess its "Stdout" variable, of type
|
||||
"*OS.FD"; given "OS.Stdout" we can use its "WriteString" method to print the string.
|
||||
"*OS.FD". The "import" statement is a declaration: it names the identifier ("OS")
|
||||
that will be used to access members of the package imported from the file ("os"),
|
||||
found in the current directory or in a standard location.
|
||||
Given "OS.Stdout" we can use its "WriteString" method to print the string.
|
||||
|
||||
The comment convention is the same as in C++:
|
||||
|
||||
@ -517,14 +520,64 @@ Now "main"'s interface to the prime sieve is a channel of primes:
|
||||
|
||||
--PROG progs/sieve1.go /func.main/ /^}/
|
||||
|
||||
Service
|
||||
Multiplexing
|
||||
----
|
||||
|
||||
here we will describe this server:
|
||||
With channels, it's possible to serve multiple independent client goroutines without
|
||||
writing an actual multiplexer. The trick is to send the server a channel in the message,
|
||||
which it will then use to reply to the original sender.
|
||||
A realistic client-server program is a lot of code, so here is a very simple substitute
|
||||
to illustrate the idea. It starts by defining "Request" type, which embeds a channel
|
||||
that will be used for the reply.
|
||||
|
||||
--PROG progs/server.go
|
||||
--PROG progs/server.go /type.Request/ /^}/
|
||||
|
||||
and this modification, which exits cleanly
|
||||
The server will be trivial: it will do simple binary operations on integers. Here's the
|
||||
code that invokes the operation and responds to the request:
|
||||
|
||||
--PROG progs/server1.go /func.Server/ END
|
||||
--PROG progs/server.go /type.BinOp/ /^}/
|
||||
|
||||
The "Server" routine loops forever, receiving requests and, to avoid blocking due to
|
||||
a long-running operation, starting a goroutine to do the actual work.
|
||||
|
||||
--PROG progs/server.go /func.Server/ /^}/
|
||||
|
||||
We construct a server in a familiar way, starting it up and returning a channel to
|
||||
connect to it:
|
||||
|
||||
--PROG progs/server.go /func.StartServer/ /^}/
|
||||
|
||||
Here's a simple test. It starts a server with an addition operator, and sends out
|
||||
lots of requests but doesn't wait for the reply. Only after all the requests are sent
|
||||
does it check the results.
|
||||
|
||||
--PROG progs/server.go /func.main/ /^}/
|
||||
|
||||
One annoyance with this program is that it doesn't exit cleanly; when "main" returns
|
||||
there are a number of lingering goroutines blocked on communication. To solve this,
|
||||
we provide a second, "quit" channel to the server:
|
||||
|
||||
--PROG progs/server1.go /func.StartServer/ /^}/
|
||||
|
||||
It passes the quit channel to the "Server" function, which uses it like this:
|
||||
|
||||
--PROG progs/server1.go /func.Server/ /^}/
|
||||
|
||||
Inside "Server", a "select" statement chooses which of the multiple communications
|
||||
listed by its cases can proceed. If all are blocked, it waits until one can proceed; if
|
||||
multiple can proceed, it chooses one at random. In this instance, the "select" allows
|
||||
the server to honor requests until it receives a quit message, at which point it
|
||||
returns, terminating its execution. (The language doesn't yet allow the ":="
|
||||
syntax in "select" statements, although it might one day. Also, observe the use
|
||||
of the binary, infix form of the receive operator.)
|
||||
|
||||
|
||||
All that's left is to strobe the "quit" channel
|
||||
at the end of main:
|
||||
|
||||
--PROG progs/server1.go /adder,.quit/
|
||||
...
|
||||
--PROG progs/server1.go /quit....true/
|
||||
|
||||
There's a lot more to Go programming and concurrent programming in general but this
|
||||
quick tour should give you some of the basics.
|
||||
|
@ -4,13 +4,13 @@
|
||||
|
||||
package main
|
||||
|
||||
type BinOp (a, b int) int;
|
||||
|
||||
type Request struct {
|
||||
a, b int;
|
||||
replyc *chan int;
|
||||
}
|
||||
|
||||
type BinOp (a, b int) int;
|
||||
|
||||
func Run(op *BinOp, request *Request) {
|
||||
result := op(request.a, request.b);
|
||||
request.replyc -< result;
|
||||
|
@ -4,13 +4,13 @@
|
||||
|
||||
package main
|
||||
|
||||
type BinOp (a, b int) int;
|
||||
|
||||
type Request struct {
|
||||
a, b int;
|
||||
replyc *chan int;
|
||||
}
|
||||
|
||||
type BinOp (a, b int) int;
|
||||
|
||||
func Run(op *BinOp, request *Request) {
|
||||
result := op(request.a, request.b);
|
||||
request.replyc -< result;
|
||||
@ -20,7 +20,7 @@ func Server(op *BinOp, service *chan *Request, quit *chan bool) {
|
||||
for {
|
||||
var request *Request;
|
||||
select {
|
||||
case request <- service: // can't say request := <-service here yet
|
||||
case request <- service:
|
||||
go Run(op, request); // don't wait for it
|
||||
case <-quit:
|
||||
return;
|
||||
|
@ -16,7 +16,7 @@ func Generate() *chan int {
|
||||
}
|
||||
|
||||
// Filter out input values divisible by 'prime', send rest to returned channel
|
||||
func Filter(in *chan int, prime int) *chan int{
|
||||
func Filter(in *chan int, prime int) *chan int {
|
||||
out := new(chan int);
|
||||
go func(in *chan int, out *chan int, prime int) {
|
||||
for {
|
||||
|
Loading…
Reference in New Issue
Block a user