For more comprehensive information: Go here, or here.
Even though:
Go is a response to these challenges
var c chan string; c = make(chan string); c <- "Hello"; // infix send // in a different goroutine greeting := <-c; // prefix receive cc := new(chan chan string); // cc a variable of type channel providing channels of type string cc <- c; // handing off a capability
func wrapper(a int, c chan int) {
result := longCalculation(a);
c <- result;
}
c := make(chan int);
go wrapper(17, c);
// do something for a while; then...
x := <-c;
Go has channels as first class objects, just as Occam does.
ch is a variable of type chan with a
`protocol' which is the type int<- operator puts i into
ch.
func generate(ch chan int) {
for i := 2; ; i++ {
ch <- i // Send 'i to channel 'ch'.
}
}
Here is another example that gets numbers from one channel and sends some of them to another.
<-, gets an int from
the channel for the assignment to i. func filter(in, out chan int, prime int) {
for {
i := <-in; // Receive value of new variable 'i' from 'in'.
if i % prime != 0 {
out <- i // Send 'i' to channel 'out'.
}
}
}
These two functions should execute simultaneously like processes joined by a Unix pipe.
Putting generate and filter together shows how
goroutines get a common channel for communication.
generate is created and will start putting natural numbers
into ch.for loop, main grabs the first number
from ch (2), and prints it.filter along with the first prime (2).filter gets the subsequent numbers from generate and
passes those not divisible by 2 to ch1.ch1 is assigned to ch and the first number
out of filter (3) is the new prime.Note that we are here creating a set of goroutines that gives a data-flow computation.
func main() {
ch := make(chan int); // Create a new channel.
go generate(ch); // Start generate() as a goroutine.
for {
prime := <-ch;
fmt.Println(prime);
ch1 := make(chan int);
go filter(ch, ch1, prime);
ch = ch1
}
}
A server listens for a request then replies with the result.
type request struct {
a, b int;
replyc chan int;
}
func server(service chan *request, quit chan bool) {
for {
select {
case req := <-service:
go worker(req); // don't wait for it
case <-quit:
return;
}
}
It then creates a worker to do the work, passing on the request including the channel for replying.
func worker(req *request) {
req.replyc <- req.a + req.b;
}
Requests can be replied to out of order.
func startServer( ) chan *request {
req := make( chan *request );
quit := make( chan bool );
go server( req, quit );
return req;
}
func main() {
adder := startServer( );
const N = 100;
var reqs [N]request;
for i := 0; i < N; i++ {
req := &reqs[i];
req.a = i;
req.b = i + N;
req.replyc = make(chan int);
adder <- req;
}
for i := N-1; i >= 0; i-- { // doesn't matter what order
if <-reqs[i].replyc != N + 2*i {
fmt.Println("fail at", i);
}
}
fmt.Println("done");
}
Making the client start the server hides a little complexity here.
The description of the Go memory model goes into the details of synchronization: it is educational in the context of cs452.
Return to: