fork, terminated by wait(
)pipe( )
socket( )
Send( ) / Receive( ) / Reply( )
Based on the concept of a channel
There has to be a way for the two processes to get hold of the same channel. Examples,
When CSP is provided by an operating system type safety is most likely to be provided at run-time.
When CSP is provided by a programming language type safety can often be provided at compile-time.
Use many co-operating, medium capability microCPUs to do a big job.
Problem is communication
Comminication requires either
These are all broadcast (local) at the hardware level
The transputer was an early, now vanished, example of the latter
channel ? variable
channel ! value // the value of a variable or the result of an procedure
clock ? now
AFTER can be used to combine times
IF now AFTER yesterday THEN
AFTER can make timer input blocking
clock ? AFTER tomorrow
You can write a type-safe server, BUT
Program structure is more static than is allowed in your system.
This might be a good thing.
For comprehensive information: Go here.
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.
make instantiates a chan intgo instantiates what they call a goroutine, which
eventually computes the sumresult := blocks until sum puts the result into the
channel it has been given, ch.wait(). ch := make(chan int);
go sum(hugeArray, ch);
// ... do something else for a while
result := <-ch; // wait for, and retrieve, result
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) {
for {
req := <-service;
go worker(req); // don't wait for it
}
}
It then creates a worker to do the work, passing on the request including the channel for replying.
func worker(req *request) {
reply := sum(req.a, req.b);
req.replyc <- reply;
}
Requests can be replied to out of order.
func startServer( ) chan *request {
req := make(chan *request);
go server(op, req);
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: