多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[TOC] # 简介 每个资源都对应一个可称为"互斥锁"的标记,这个标记用来保证在任意时刻,只有一个协程(线程)访问该资源.其他的协程只能等待 由标准库sync中的Mutex结构体类型表示. `sync.Mutex`类型只有两个公开的指针方法,Lock和Unlock. Lock锁定当前的共享资源,Unlock进行解锁 使用互斥锁时,一定要注意:对资源操作完成后,一定要解锁,否则出现流程执行异常,死锁问题.通常借助defer.锁定后,立即使用defer语句保证互斥锁及时解锁 ~~~ var mutex sync.Mutex //定义互斥锁变量 func write() { muter.Lock() defer muter.Unlock() } ~~~ # 使用 ~~~ //新创建为0.表示没有加锁,锁只有1把 var mutex sync.Mutex func printer(str string) { mutex.Lock() for _, ch := range str { fmt.Println(string(ch)) time.Sleep(300 * time.Millisecond) } //共享数据访问结束 mutex.Unlock() } func person1() { printer("11") } func person2() { printer("222") } func main() { //先,因为他先获得锁 go person1() //后 go person2() for { ; } } ~~~ # 不是强制性的 互斥锁的使用是主动控制互斥锁,即一个愿打一个愿挨。比如即使一个routine里用了Lock(),但在另一个routine可以不理会这个锁就能访问这个struct,只需要不调用Lock()就行。例子如下 ~~~ package main import ( "fmt" "sync" "time" ) type SafeCounter struct { v map[string]int mux sync.Mutex } func (c *SafeCounter) Inc(key string) { c.mux.Lock() time.Sleep(3 * time.Second) c.v[key]++ c.mux.Unlock() } func main() { c := SafeCounter{v: make(map[string]int)} go c.Inc("somekey") time.Sleep(1 * time.Second) c.v["somekey"]++ fmt.Println(c.v["somekey"]) time.Sleep(3 * time.Second) fmt.Println(c.v["somekey"]) } ~~~ 输出 ~~~ 1 2 ~~~ `c.v["somekey"]++`之前,是已经锁了,本应该等2-3秒,才能输出,但是从程序开始运行,只过了1秒就输出了,说明Lock只是一种人为的互斥,是一种协议,并不是强制。 # 已经锁定的Mutex与特定的goroutine无关联 已经锁定的Mutex并不与特定的goroutine相关联,这样可以利用一个goroutine对其加锁,再利用其他goroutine对其解锁,例子如下 ~~~ package main import ( "fmt" "sync" "time" ) type MyStruct struct { v int mux sync.Mutex } func (s *MyStruct) Lock() { s.mux.Lock() } func (s *MyStruct) Unlock() { s.mux.Unlock() } func main() { s := MyStruct{v: 0} s.v = 1 fmt.Printf("%+v\n", s) go s.Lock() time.Sleep(1 * time.Second) fmt.Printf("%+v\n", s) go s.Unlock() time.Sleep(1 * time.Second) fmt.Printf("%+v\n", s) } ~~~ 输出 ~~~ {v:1 mux:{state:0 sema:0}} {v:1 mux:{state:1 sema:0}} {v:1 mux:{state:0 sema:0}} ~~~ 可以看出,可以一个routine里锁定,另一个routine里解锁。因为锁只和具体变量关联,和routine无关,只要这个变量是共享的,比如通过指针传递,或者全局变量都可以。 虽然互斥锁可以被直接的在多个Goroutine之间共享,但是我们还是强烈建议把对同一个互斥锁的成对的锁定和解锁操作放在同一个层次的代码块中。例如,在同一个函数或方法中对某个互斥锁的进行锁定和解锁。