Golang学习笔记-sync

Mutex

  • sync.Mutex为互斥锁,同一时间只能有一个goroutine获得互斥锁。
  • 使用Lock()加锁,Unlock()解锁,加锁前不能解锁,加锁后不能继续加锁。
  • 已经锁定的 Mutex 并不与特定的 goroutine 相关联,可以利用一个 goroutine 对其加锁,再利用其他 goroutine 对其解锁。
  • 适用于同一时间只能有一个goroutine访问资源的场景。

下面的代码如果不使用Mutex,输出的会是1 1 2 2 3 3而不是1 2 3 1 2 3。

package main

import (
	"fmt"
	"sync"
	"time"
)

var (
	mutex sync.Mutex
)

func print123(){
	mutex.Lock()
	defer mutex.Unlock()
	for i:=0;i<3;i++{
		fmt.Println(i+1)
		time.Sleep(time.Millisecond*100)
	}
}

func main(){
	go print123()
	go print123()
	time.Sleep(time.Second*5)
}

RWMutex

  • sync.RWMutex为读写锁,同一时间可以有多个goroutine获得读锁或者一个goroutine获得写锁。
  • 使用Lock()加写锁,Unlock()解写锁,RLock()加读锁,RUnlock()解读锁,读锁可以加多个,解读锁的次数不能多于加读锁的次数。
  • 适用于同一时间可以有多个goroutine对资源进行读操作或一个goroutine对资源进行写操作的场景。
  • 读少写多的情况下使用Mutex,其它情况下RWMutex性能更好。

Map

Go内建的map不是线程安全的,所以之前都会使用加锁的方式控制对map的并发访问,而在新版本中可以使用sync.Map代替map+RWMutex,sync.Map相比后者有更好的性能,下面是sync.Map的基本使用方法:

package main

import (
	"fmt"
	"sync"
)

var (
	players sync.Map
)



func main(){
	//设置key对应的value
	players.Store("xiao ming",100)
	//返回key对应的valuee,value不存在时返回nil,false
	if value,ok:=players.Load("xiao ming");ok{
		fmt.Println(value)
	}else{
		fmt.Println("key:xiao ming does not exist!")
	}
	//如果key对应的value存在,则返回value和true
	//如果key对应的value不存在,则将key对应的value设置为参数中的value,并返回value和false
	if value,ok := players.LoadOrStore("xiao hong",120);ok{
		fmt.Println(value)
	}else{
		fmt.Println("key:xiao hong does not exist,store it!")
	}
	//遍历map,函数返回false时停止遍历
	players.Range(func(key,value interface{})bool{
		fmt.Println(key,value)
		return true
	})
	//删除key对应的value
	players.Delete("xiao hong")
}

atomic

sync.atomic提供了对几种简单类型进行原子操作的函数(LoadXXX,StoreXXX),相比Mutex/RWMutex,其性能更好,临界区也更小。

Once

sync.Once可以使函数只被调用一次:

package main

import (
	"fmt"
	"sync"
	"time"
)

var (
	closeOnce sync.Once
)

func closeSomething(){
	fmt.Println("close object")
}

func invokeClose(){
	closeOnce.Do(closeSomething)
}

func main() {
	go invokeClose()
	go invokeClose()
	time.Sleep(time.Second*5)
}

WaitGroup

sync.WaitGroup常用来等待其它goroutine结束,Add()增加计数器,Done()减少计数器,Wait()会一直阻塞当前goroutine直到计数器为0。

常见用法为调用Add()增加计数器,启动goroutine,调用Wait()等待goroutine结束,goroutine结束时调用Done()减少计数器。

需要注意的是不能让计数器为负数,否则会报错。另外sync.WaitGroup是值类型,所以在传值的时候需要用指针。

package main

import (
	"sync"
	"time"
)

var (
	waitGroup = &sync.WaitGroup{}
)

func doSomething(){
	defer waitGroup.Done()
	time.Sleep(time.Second*5)
}



func main() {
	waitGroup.Add(1)
	go doSomething()
	waitGroup.Wait()
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章