Skip to content

Instantly share code, notes, and snippets.

@dlemfh
Last active June 6, 2022 16:34
Show Gist options
  • Save dlemfh/3252281a7a427b7e3efcf3fc8456677c to your computer and use it in GitHub Desktop.
Save dlemfh/3252281a7a427b7e3efcf3fc8456677c to your computer and use it in GitHub Desktop.

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)
}

image


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)
}

image


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)
}

image


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)
}

image

@dlemfh
Copy link
Author

dlemfh commented Jun 6, 2022

* 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."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment