Skip to content

Instantly share code, notes, and snippets.

@Patrikios
Forked from xin053/sync.md
Created September 13, 2022 15:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Patrikios/641115068ba03eaa26b7e57f628c7bc6 to your computer and use it in GitHub Desktop.
Save Patrikios/641115068ba03eaa26b7e57f628c7bc6 to your computer and use it in GitHub Desktop.
[go sync] go sync #go #sync

sync.WaitGroup

package main

import (
	"log"
	"math/rand"
	"sync"
	"time"
)

func main() {
	rand.Seed(time.Now().UnixNano())

	const N = 5
	var values [N]int32

	var wg sync.WaitGroup
	wg.Add(N)
	for i := 0; i < N; i++ {
		i := i
		go func() {
			values[i] = 50 + rand.Int31n(50)
			log.Println("Done:", i)
			wg.Done() // <=> wg.Add(-1)
		}()
	}

	wg.Wait()
	// All elements are guaranteed to be
	// initialized now.
	log.Println("values:", values)
}

sync.Once

package main

import (
	"log"
	"sync"
)

func main() {
	log.SetFlags(0)

	x := 0
	doSomething := func() {
		x++
		log.Println("Hello")
	}

	var wg sync.WaitGroup
	var once sync.Once
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			once.Do(doSomething)
			log.Println("world!")
		}()
	}

	wg.Wait()
	log.Println("x =", x) // x = 1
}

sync.Mutex and sync.RWMutex

Both of the *sync.Mutex and *sync.RWMutex types implement the sync.Locker interface. So they both have two methods, Lock and Unlock, to prevent multiple data users from using a piece of data concurrently.

A Mutex value is often called a mutual exclusion lock. A zero Mutex value is an unlocked mutex. A Mutex value can only be locked when it is in unlocked status. In other words, once an addressable Mutex value m is locked successfully (a.k.a., a m.Lock() method call returns), a new attempt by a goroutine to lock the Mutex value will make the goroutine enter blocking state, until the Mutex value is unlocked (through a later m.Unlock() call).

Please note that m.Lock() and m.Unlock() are shorthands of (&m).Lock() and (&m).Unlock(), respectively.

package main

import (
	"fmt"
	"runtime"
	"sync"
)

type Counter struct {
	m sync.Mutex
	n uint64
}

func (c *Counter) Value() uint64 {
	c.m.Lock()
	defer c.m.Unlock()
	return c.n
}

func (c *Counter) Increase(delta uint64) {
	c.m.Lock()
	c.n += delta
	c.m.Unlock()
}

func main() {
	var c Counter
	for i := 0; i < 100; i++ {
		go func() {
			for k := 0; k < 100; k++ {
				c.Increase(1)
			}
		}()
	}

	// The loop is just for demo purpose.
	for c.Value() < 10000 {
		runtime.Gosched()
	}
	fmt.Println(c.Value()) // 10000
}

RWMutex 的存在主要是对读优化,我们这样定义:

  • 写锁定(lock),对读写操作进行锁定
  • 写解锁(Unlock),对写锁定进行解锁
  • 读锁定(RLock),对读操作进行锁定
  • 读解锁(RUnlock),对读锁定进行解锁

对于一个RWMutexm,遵循规则:

  • 同时只有一个 goroutine 能够获得写锁定,当一个 goroutine 获得写锁定之后,其他无论是读锁定还是写锁定都将阻塞直到写锁定解锁
  • 同时可以有任意多个 goroutine 获得读锁定,当一个 goroutine 获得读锁定之后,其他 goroutine 也能获得读锁定,但是写锁定将等待所有读锁定解锁之后才能够获取写锁定
  • 不能同时获得写锁定和读锁定(读和写互斥)
  • 假设 m 的读锁定已经被获取,在队列中已经有写锁定被阻塞,如果这时候有其他 goroutine 需要读锁定,为了避免循环读锁定,这个 goroutine 将被阻塞,m的读锁定被释放后,队列中那个需要写锁定的 goroutine 将被执行
  • 假设 m 的写锁定已经被获取,在队列中已经有读锁定被阻塞,如果这时候有其他 goroutine 需要写锁定,为了避免循环写锁定,这个 goroutine 将被阻塞,m的写锁定被释放后,队列中那个需要读锁定的 goroutine 将被执行
package main

import (
	"fmt"
	"runtime"
	"sync"
)

type Counter struct {
	m nc.RWMutex
	n uint64
}

func (c *Counter) Value() uint64 {
	c.m.RLock()
	defer c.m.RUnlock()
	return c.n
}

func (c *Counter) Increase(delta uint64) {
	c.m.Lock()
	c.n += delta
	c.m.Unlock()
}

func main() {
	var c Counter
	for i := 0; i < 100; i++ {
		go func() {
			for k := 0; k < 100; k++ {
				c.Increase(1)
			}
		}()
	}

	// The loop is just for demo purpose.
	for c.Value() < 10000 {
		runtime.Gosched()
	}
	fmt.Println(c.Value()) // 10000
}

sync.Map

package main

import (
	"fmt"
	"sync"
)

func main() {
	var m sync.Map

	//Store
	m.Store("name", "Joe")
	m.Store("gender", "Male")

	//LoadOrStore
	//若key不存在,则存入key和value,返回false和输入的value
	v, ok := m.LoadOrStore("name1", "Jim")
	fmt.Println(ok, v) //false Jim

	//若key已存在,则返回true和key对应的value,不会修改原来的value
	v, ok = m.LoadOrStore("name", "aaa")
	fmt.Println(ok, v) //true Joe

	//Load
	v, ok = m.Load("name")
	if ok {
		fmt.Println("key存在,值是: ", v)
	} else {
		fmt.Println("key不存在")
	}

	//Range
	//遍历sync.Map
	f := func(k, v interface{}) bool {
		fmt.Println(k, v)
		return true
	}
	m.Range(f)

	//Delete
	m.Delete("name1")
	fmt.Println(m.Load("name1"))

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