Make sure that all messages are printed:
func main() {
go say("hello")
go say("world")
time.Sleep(3 * time.Second)
}
WaitGroup
import "sync"
func main() {
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
say("hello")
wg.Done()
}()
say("world")
wg.Wait()
}
package main
import "time"
var counter = 0
func add(i int) {
counter += i
}
func main() {
for {
println(counter)
// Hammer the counter!
for i := 0; i < 1000; i++ {
go add(1)
go add(-1)
}
// Let the dust settle
time.Sleep(time.Second)
}
}
counter
variable at any given moment.This also works in Go, but it's not idiomatic. Instead:
Don't communicate by sharing memory;
share memory by communicating.
The core communication device is a channel.
Channels are typed and (optionally) buffered:
message := make(chan string, 1)
numbers := make(chan int, 10)
ready := make(chan bool) // same as make(chan bool, 0)
message <- "Hello world!"
s := <-message
fmt.Println(s)
t := <-message // blocked
message <- "Hello"
message <- "world! // blocked
numbers <- 0
numbers <- 1
numbers <- 2
fmt.Println(<-numbers)
fmt.Println(<-numbers)
fmt.Println(<-numbers)
fmt.Println(<-numbers) // blocked
Unbuffered channels seem useless at first sight
ready <- true //
status := <-ready //
... but they actually shine
in a concurrent setting
as a synchronisation mecanism!
var ready = make(chan bool)
func Greetings() {
for i:=0; i<10; i++ {
fmt.Println("Hello world!")
}
ready <- true
}
func main() {
fmt.Println("begin")
go Greetings()
<-ready // Wait for the end!
fmt.Println("end")
}
var c = make(chan int, 2)
func sum(s []int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
go sum(s[:len(s)/2])
go sum(s[len(s)/2:])
x, y := <-c, <-c
println(x, y, x+y)
}
func throttled(input chan string) chan string {
output := make(chan string)
go func() {
for {
output <- <-input
time.Sleep(3 * time.Second)
}
}()
return output
}
func main() {
input := make(chan string)
output := throttled(input)
for i:=0; i<10; i++ {
input <- "Hello world!"
fmt.Println(<-output)
}
}
Channels: safe to use concurrently
var counter = 0
var ch = make(chan int, 100)
func add(i int) {
ch <- i
}
func counterHandler() {
for {
counter += <-ch
}
}
func main() {
go counterHandler() // New!
go display() //
for {
time.Sleep(time.Second) //
// Hammer the counter!
for i := 0; i < 1000; i++ {
go add(1)
go add(-1)
}
}
}
Sequential implementation of two concurrent processes:
for {
for i:=0; i < 10; i++ {
// Executed at ~10Hz
fmt.Println("FAST")
time.Sleep(time.Second / 10)
}
// Executed at ~1Hz
fmt.Println("SLOW")
}
Consider instead
func Printer(m string, d time.Duration) {
for {
fmt.Print(m)
time.Sleep(d)
}
}
func main() {
go Printer("FAST", time.Second / 10)
Printer("SLOW", time.Second)
}
Even better
func Printer(m string, d time.Duration) {
for {
wait := time.After(d)
fmt.Println(m)
<-wait
}
}
func main() {
go Printer("FAST", time.Second / 10)
Printer("SLOW", time.Second)
}
or use the time
module Ticker
& Timer
API!
func HappyNewYear(year int) chan struct{} {
trigger := make(chan struct{})
newYear := time.Date(
year, time.January,
0, 0, 0, 0, 0, time.UTC)
go func() {
for {
if time.Now().After(newYear) {
fmt.Println(" Happy New Year!")
trigger <- struct{}{}
return
}
time.Sleep(time.Second)
}
}()
return trigger
}
func main() {
<-HappyNewYear(2024)
}
## TODO - Mention Actor model ("Subject" vs "Object")