Channels are first-class values, which means they can be sent over channels. This property makes it easy to write a service multiplexer since the client can supply, along with its request, the channel on which to reply.
chanOfChans := make(chan chan int)
Or more typically
type Reply struct { … }
type Request struct {
arg1, arg2, arg3 some_type;
replyc chan [...]
Channels are first-class values, which means they can be sent over channels. This property makes it easy to write a service multiplexer since the client can supply, along with its request, the channel on which to reply.
chanOfChans := make(chan chan int)
Or more typically
type Reply struct { … }
type Request struct {
arg1, arg2, arg3 some_type;
replyc chan *Reply;
}
Multiplexing server
type request struct {
a, b int;
replyc chan int;
}
type binOp func(a, b int) int
func run(op binOp, req *request) {
req.replyc <- op(req.a, req.b)
}
func server(op binOp, service chan *request) {
for {
req := <-service; // requests arrive here
go run(op, req); // don't wait for op
}
}
Starting the server
Use the channel function pattern to create a channel to a new server:
func startServer(op binOp) chan *request {
req := make(chan *request);
go server(op, req);
return req
}
var adderChan = startServer(
func(a, b int) int { return a + b }
)
The client
A similar example is worked in more detail in the tutorial, but here’s a variant:
func (r *request) String() string {
return fmt.Sprintf(”%d+%d=%d”,
r.a, r.b, <-r.replyc)
}
req1 := &request{ 7, 8, make(chan int) };
req2 := &request{ 17, 18, make(chan int) };
Requests ready; send them:
adderChan <- req1;
adderChan <- req2;
Can retrieve results in any order; r.replyc demuxes:
fmt.Println(req2, req1);
Teardown
In the mux example, the server runs forever. To tear it down cleanly, signal with a channel. This server has the same functionality but with a quit channel:
func server(op binOp, service chan *request,
quit chan bool) {
for {
select {
case req := <-service:
go run(op, req); // don’t wait for it
case <-quit:
return;
}
}
}
Starting the server
The rest of the code is mostly the same, with an extra channel:
func startServer(op binOp) (service chan *request,
quit chan bool) {
service = make(chan *request);
quit = make(chan bool);
go server(op, service, quit);
return service, quit;
}
var adderChan, quitChan := startServer(
func(a, b int) int { return a + b }
)
Teardown: the client
The client is unaffected until it’s ready to shut down the server:
req1 := &request{7, 8, make(chan int)};
req2 := &request{17, 18, make(chan int)};
adderChan <- req1;
adderChan <- req2;
fmt.Println(req2, req1);
All done, signal server to exit:
quitChan <- true;
Chaining
package main
import (”flag”; “fmt”)
var ngoroutine = flag.Int(”n”, 100000, “how many”)
func f(left, right chan int) { left <- 1 + <-right }
func main() {
flag.Parse();
leftmost := make(chan int);
var left, right chan int = nil, leftmost;
for i := 0; i < *ngoroutine; i++ {
left, right = right, make(chan int);
go f(left, right);
}
right <- 0; // bang!
x := <-leftmost; // wait for completion
fmt.Println(x); // 100000
}
Example: Leaky bucket
Queue of shared, reusable buffers
var freeList = make(chan *Buffer, 100)
var serverChan = make(chan *Buffer)
func server() {
for {
b := <-serverChan; // wait for work
process(b);
ok := freeList <- b // reuse buffer if room
}
}
func client() {
for {
b, ok := <-freeList; // grab one if available
if !ok { b = new(Buffer) }
load(b);
serverChan <- b // send to server
}
}
No related posts.







Leave Your Response