Concurrency Demo using Go
cores.go
(Check # of cores)
package main
import (
"fmt"
"runtime"
)
func main () {
numCPU := runtime .NumCPU ()
maxProcs := runtime .GOMAXPROCS (0 )
fmt .Println ("numCPU" , numCPU )
fmt .Println ("maxProcs" , maxProcs )
}
without-mutex.go
(Incorrect results using goroutines w/o synchronization)
package main
import (
"fmt"
"sync"
"time"
)
// shared resource
var resource = 0
// mutex object
// var mutex sync.Mutex
func goroutine (wg * sync.WaitGroup ) {
defer wg .Done ()
// mutex.Lock()
resource = resource + 1 // (critical section)
// mutex.Unlock()
}
func main () {
start := time .Now ()
var wg sync.WaitGroup
for i := 0 ; i < 10000 ; i ++ {
wg .Add (1 )
go goroutine (& wg )
}
wg .Wait ()
took := time .Since (start )
fmt .Println ("# without mutex" )
fmt .Println ("resource:" , resource ) // (should be 10000)
fmt .Println ("took:" , took )
}
using-mutex.go
(Synchronize using optimized built-in mutex)
package main
import (
"fmt"
"sync"
"time"
)
// shared resource
var resource = 0
// mutex object
var mutex sync.Mutex
func goroutine (wg * sync.WaitGroup ) {
defer wg .Done ()
mutex .Lock ()
resource = resource + 1 // (critical section)
mutex .Unlock ()
}
func main () {
start := time .Now ()
var wg sync.WaitGroup
for i := 0 ; i < 10000 ; i ++ {
wg .Add (1 )
go goroutine (& wg )
}
wg .Wait ()
took := time .Since (start )
fmt .Println ("# using mutex" )
fmt .Println ("resource:" , resource )
fmt .Println ("took:" , took )
}
bakery.go
(Implementation of mutex using Bakery Algorithm)
package main
import (
"fmt"
"sync"
"time"
)
// shared resource
var resource = 0
// shared non-atomic SWMR registers
var FLAG [10000 ]bool
var MY_TURN [10000 ]int
// mutex implementation (bakery algorithm)
func Lock (i int ) {
FLAG [i ] = true
max := 0
for j := range MY_TURN {
if MY_TURN [j ] > max {
max = MY_TURN [j ]
}
}
MY_TURN [i ] = max + 1
FLAG [i ] = false
for j := range MY_TURN {
if i == j {
continue
}
for ! (FLAG [j ] == false ) {
}
for ! ((MY_TURN [j ] == 0 ) || (MY_TURN [i ] < MY_TURN [j ] || (MY_TURN [i ] == MY_TURN [j ] && i < j ))) {
}
}
}
func Unlock (i int ) {
MY_TURN [i ] = 0
}
func goroutine (i int , wg * sync.WaitGroup ) {
defer wg .Done ()
Lock (i )
resource = resource + 1 // (critical section)
Unlock (i )
}
func main () {
start := time .Now ()
var wg sync.WaitGroup
for i := 0 ; i < 10000 ; i ++ {
wg .Add (1 )
go goroutine (i , & wg )
}
wg .Wait ()
took := time .Since (start )
fmt .Println ("# bakery algorithm" )
fmt .Println ("resource:" , resource )
fmt .Println ("took:" , took )
}
* For most cases, there's no need to implement mutexes yourself. Trust the optimized version provided by Go.
Better yet, "Do not communicate by sharing memory; instead, share memory by communicating."