PCLT - Channel-based Concurrency Module
Lab Class #1 (Clocks, Cats and Primes)
Your assignment repository contains some startup files for the first lab. assignment of the Go-based module.
You have 2 small problems to solve, which will act as a bit of a warm-up to programming in Go.
To submit your answers, simply push your files onto the repository. Some problems will require you to modify existing files and add new ones. The problems will not be graded, but we will use a similar system for the mini-project next week and the project the week after.
Setup
Naturally, you will need to have a recent
Go distribution installed on your
machine. You can check that the go
tool is available on your
shell’s path by typing go version
.
Go Modules
From Go version 1.11 and above, the go
tool now supports so-called
modules, Go’s dependency management system and build
system. Previously, the go
tool relied on the environment variable
GOPATH
to find sources and dependencies to build.
The general setup of a go project with modules is as follows:
Create a folder for your project (outside of
GOPATH
). For example, lets create a folderplayground
(mkdir playground
).In the folder, run the command
go mod init MyApp
. This will setup a new module calledMyApp
by creating ago.mod
file with the contents (your go version may vary):module MyApp go 1.15
The go build tool will update this file with any external
dependencies that are used in the module. The significance of
the module name is that all import paths in the module begin
with the module name, even if the folder has a completely
different name from the module name. A common idiom is to use a
module name of the form github.com/btoninho/myproject
. You
can then use commands such as go build
or go test
on
your module.
There is often some confusion as to how packages work inside a module (or in go in general). While it is good practice to have package and folders name match, this need not be the case (and is not enforced by the go compiler). A package name is specified by the
package
declaration at the top of a go source file. The compiler expects that all files in the same folder live in the same package. The compiler will complain if you delcare more than one package in the same folder. For instance, the following two filesf1.go
andf2.go
, if included in the same folder, will result in a build error:f1.go ------------ package p1 func f1() {} f2.go ------------ package p2 func f2() {}
If the module includes an executable entrypoint, Go requires the program entrypoint to be in a specially named package
main
. This will generally be the package at the root of your project. All other packages will exist in subfolders of your module root. For instance, a module with amain
package and another package would have the following dir. structure and contents:λ tree . ├── go.mod ├── hello_world.go └── my_other_package └── solver.go 1 directory, 3 files λ cat hello_world.go package main import "fmt" func main() { fmt.Println("Hello world.") } λ cat my_other_package/solver.go package my_other_package func Solve() { //... }
To use the
my_other_package
in themain
package, we use:package main import ( "MyApp/my_other_package" "fmt" ) func main() { my_other_package.Solve() fmt.Println("Hello world.") }
Note that the import path is of the form
module_name/package_dir
. To use the package’s exported contents, usepackage_name.function_name
orpackage_name.type
. This is important if by some reason the package name differs from the directory name. Go also supports qualified imports, where we locally rename an imported package. Look it up!
Now that we know how to use go modules, lets do some minor hacking.
Problem 1 - Clocks and Cats
The file clockserver/clockserver.go
contains a simple, non-concurrent implementation of a clock server. The clock server listens for TCP connections on port 8080
. Once a connection is established, the server sends the current time every second. The server is not programmed to do any sort of sophisticated error handling, simply logging any errors as they arise.
Try to build and run the clock server. To see what the server is sending, use the command nc localhost 8080
if you are using an Unix-based machine or (build and run) the netcat/netcat.go
program. The supplied netcat
is hardcoded to listen on port 8080
.
- You may have noticed that the clock server can only handle a single client at a time. Make the clock server accept requests concurrently. This should be very easy.
- Modify the clock server to accept a port number and write a client program (place it under
clockclient/clockclient.go
) that receives from multiple clocks simultaneously (e.g. in different timezones), displaying the results in some reasonably formated way. For the client to know on which address to listen to, use a reasonable command-line argument syntax (e.g.clockclient [ClockName=ip:port]+
).
Note: In a Unix-based system you can “fake” the timezone of the clock server by changing the environment variable TZ
. For instance, TZ=Asia/Seoul ./clockserver
will run a clockserver with the timezone of Seoul, South Korea.
Problem 2 - Concurrent Primes
Now that you are a bit more familiar with Go, its time to focus a on channels.
Write a pipeline of (infinitely running) goroutines that calculate
and subsequently print out
prime numbers, in sequence, using a prime
sieve. Place
your implementation under primes/sieve.go
.
You will likely want to structure your program using three “kinds”
of goroutines (connected using channels): an initial Producer
routine that simply emits the stream of all natural numbers
(starting at 2) in sequence; a chain of Sieve
routines,
connected by channels, that
forwards the number stream (i.e., the first Sieve
receives from
the Producer
and sends to the next Sieve
, and so on),
filtering out numbers that are divisible by a given (prime) number;
and, an assembling routine that implements the chaining of
Sieves
and prints out the numbers in succession.
You may want to alter the implementation described above so that it can be tested. Write a (very inefficient) test that validates a few of the output numbers. Don’t worry, we will cover (some) testing later in lecture!
That’s it! Don’t forget to push!