14/11/2020 - GO
In this example we are going to run a goroutine which prints dots on the terminal every second. The important thing here is that, we are going to manually interact with it. We will start, pause, resume and terminate it. Although it works as expected, it can be improved and the possible improvements have been added to the code at the top but we are going to ignore it for now. This is just the bare minimum skeleton.
package main
import (
"log"
"runtime"
"time"
)
/**
The initial state of a work can only be started.
If there is no work started yet then obviously there is nothing to be paused/resumed/terminated.
Valid state progressions:
start > pause
> terminate
pause > resume
> terminate
resume > pause
> terminate
Based on the progression rules above, the previous state should be recorded and checked against the
new state. If it is an invalid progression, it should be handled appropriately. e.g., nop with a warning
*/
type state string
const (
start state = "started"
pause state = "paused"
resume state = "resumed"
terminate state = "terminated"
)
func main() {
signaller := make(chan state)
// This will print 1 because main() function is a goroutine
log.Println(runtime.NumGoroutine())
go handler(signaller)
// This will print 2 because we started handler() goroutine above
log.Println(runtime.NumGoroutine())
signaller<- start
time.Sleep(time.Second*3)
// This will print 3 because handler() goroutine will start work() goroutine
log.Println(runtime.NumGoroutine())
signaller<- pause
time.Sleep(time.Second*5)
// This will print 2 because handler() goroutine will stop work() goroutine
log.Println(runtime.NumGoroutine())
signaller<- resume
time.Sleep(time.Second*3)
// This will print 3 because handler() goroutine will again start work() goroutine
log.Println(runtime.NumGoroutine())
signaller<- terminate
time.Sleep(time.Second*3)
// This will print 1 because handler() goroutine will stop work() goroutine and then itself
log.Println(runtime.NumGoroutine())
}
func handler(signaller chan state) {
done := make(chan struct{})
for {
signal := <-signaller
switch signal {
case start:
log.Println(signal)
go work(done)
case pause:
done<- struct{}{}
log.Println(signal)
case resume:
log.Println(signal)
go work(done)
case terminate:
done<- struct{}{}
log.Println(signal)
return
default:
log.Println("unknown signal")
return
}
}
}
func work(done <-chan struct{}) {
for {
select {
case <-done:
return
default:
time.Sleep(time.Second)
log.Println(".")
}
}
}
$ go run -race main.go
2020/11/14 12:43:14 1
2020/11/14 12:43:14 2
2020/11/14 12:43:14 started
2020/11/14 12:43:15 .
2020/11/14 12:43:16 .
2020/11/14 12:43:17 3
2020/11/14 12:43:17 .
2020/11/14 12:43:17 paused
2020/11/14 12:43:22 2
2020/11/14 12:43:22 resumed
2020/11/14 12:43:23 .
2020/11/14 12:43:24 .
2020/11/14 12:43:25 3
2020/11/14 12:43:25 .
2020/11/14 12:43:25 terminated
2020/11/14 12:43:28 1