There are two sync locks Mutex and RWMutex, Mutex read and write must be queued, only a complete proceeding to the next, RWMutex parallel read and write can only be carried out one by one, when a reading is required of all after reading all closed to write, write
, the need to wait for the write operation to complete a read operation (parallel read and write single). (Sync, there is a map object is thread safe, the default map is not thread safe).
Mutex
Look at the following code:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
go setm(1)
time.Sleep(time.Millisecond * 1)
go setm(2)
time.Sleep(time.Millisecond * 1)
go setm(3)
time.Sleep(time.Millisecond * 1)
go setm(4)
time.Sleep(time.Millisecond * 1)
time.Sleep(time.Minute)
}
operation result:
---- 2019-06-11 15:55:15.053098 +0800 CST m=+0.001952900 setm 1
---- 2019-06-11 15:55:15.0630003 +0800 CST m=+0.011855100 setm1 1
---- 2019-06-11 15:55:15.0550915 +0800 CST m=+0.003946400 setm 2
---- 2019-06-11 15:55:15.0589531 +0800 CST m=+0.007807900 setm 4
---- 2019-06-11 15:55:15.0569997 +0800 CST m=+0.005854500 setm 3
---- 2019-06-11 15:55:16.0637775 +0800 CST m=+1.012632400 setm1 2
---- 2019-06-11 15:55:17.0644006 +0800 CST m=+2.013255400 setm1 4
---- 2019-06-11 15:55:18.0651814 +0800 CST m=+3.014036200 setm1 3
You can see from the above output, after code lock is blocked, behaves like a queue, go in, first out, one after the other to complete.
RWMutex
This is a read-write locks can be complicated to read, single write, mainly used in the case of reading and writing less.
Look at the following piece of code:
package main
import (
"fmt"
"sync"
"time"
)
var data = map[string]int{
"a": 0,
}
var lock sync.RWMutex
func main() {
go get(1)
time.Sleep(time.Millisecond * 1)
go get(2)
time.Sleep(time.Millisecond * 1)
go set(1)
time.Sleep(time.Millisecond * 1)
go set(2)
time.Sleep(time.Millisecond * 1)
go get(3)
time.Sleep(time.Millisecond * 1)
go set(3)
time.Sleep(time.Millisecond * 1)
go set(4)
time.Sleep(time.Millisecond * 1)
go get(4)
time.Sleep(time.Minute)
}
func get(index int) {
fmt.Println("----", time.Now(), "get", index, data["a"])
lock.RLock()
defer lock.RUnlock()
fmt.Println(time.Now(), "get", data["a"])
time.Sleep(time.Second * 1)
}
func set(a int) {
fmt.Println("----", time.Now(), "set", a, data["a"])
lock.Lock()
defer lock.Unlock()
fmt.Println(time.Now(), "set", a)
data["a"] = a
time.Sleep(time.Second * 1)
}
operation result:
---- 2019-06-11 15:59:54.1919927 +0800 CST m=+0.002940900 get 1 0
2019-06-11 15:59:54.2015089 +0800 CST m=+0.012457100 get 0
---- 2019-06-11 15:59:54.1936932 +0800 CST m=+0.004641400 get 2 0
2019-06-11 15:59:54.2015089 +0800 CST m=+0.012457100 get 0
---- 2019-06-11 15:59:54.1956461 +0800 CST m=+0.006594300 set 1 0
---- 2019-06-11 15:59:54.1966613 +0800 CST m=+0.007609600 set 2 0
---- 2019-06-11 15:59:54.1985726 +0800 CST m=+0.009520800 get 3 0
---- 2019-06-11 15:59:54.200525 +0800 CST m=+0.011473200 set 3 0
---- 2019-06-11 15:59:54.2023586 +0800 CST m=+0.013306800 set 4 0
---- 2019-06-11 15:59:54.2036319 +0800 CST m=+0.014580200 get 4 0
2019-06-11 15:59:55.2027123 +0800 CST m=+1.013660500 set 1
2019-06-11 15:59:56.202904 +0800 CST m=+2.013852200 get 1
2019-06-11 15:59:56.202904 +0800 CST m=+2.013852200 get 1
2019-06-11 15:59:57.2032109 +0800 CST m=+3.014159100 set 2
2019-06-11 15:59:58.2037928 +0800 CST m=+4.014741000 set 3
2019-06-11 15:59:59.2046672 +0800 CST m=+5.015615500 set 4
We can see from the operating results, and can have multiple read, but only one write when write, read and obstruction.
From the above operating results look a little strange, obviously two ligatures write operation, a read operation is performed, however, only after a write operation, it had all read and write during the rest of the operating.
We look golang Implemented RWMutex of:
/*读写锁的定义*/
type RWMutex struct {
w Mutex // held if there are pending writers
writerSem uint32 // semaphore for writers to wait for completing readers
readerSem uint32 // semaphore for readers to wait for completing writers
readerCount int32 // number of pending readers
readerWait int32 // number of departing readers
}
func (rw *RWMutex) RLock() {
if race.Enabled {
_ = rw.w.state
race.Disable()
}
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// A writer is pending, wait for it.
runtime_Semacquire(&rw.readerSem)
}
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
}
}
func (rw *RWMutex) RUnlock() {
if race.Enabled {
_ = rw.w.state
race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
race.Disable()
}
if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
if r+1 == 0 || r+1 == -rwmutexMaxReaders {
race.Enable()
throw("sync: RUnlock of unlocked RWMutex")
}
// A writer is pending.
if atomic.AddInt32(&rw.readerWait, -1) == 0 {
// The last reader unblocks the writer.
runtime_Semrelease(&rw.writerSem, false)
}
}
if race.Enabled {
race.Enable()
}
}
func (rw *RWMutex) Lock() {
if race.Enabled {
_ = rw.w.state
race.Disable()
}
// First, resolve competition with other writers.
rw.w.Lock()
// Announce to readers there is a pending writer.
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// Wait for active readers.
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
runtime_Semacquire(&rw.writerSem)
}
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
race.Acquire(unsafe.Pointer(&rw.writerSem))
}
}
func (rw *RWMutex) Unlock() {
if race.Enabled {
_ = rw.w.state
race.Release(unsafe.Pointer(&rw.readerSem))
race.Release(unsafe.Pointer(&rw.writerSem))
race.Disable()
}
// Announce to readers there is no active writer.
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
if r >= rwmutexMaxReaders {
race.Enable()
throw("sync: Unlock of unlocked RWMutex")
}
// Unblock blocked readers, if any.
for i := 0; i < int(r); i++ {
runtime_Semrelease(&rw.readerSem, false)
}
// Allow other writers to proceed.
rw.w.Unlock()
if race.Enabled {
race.Enable()
}
}
Read lock (RLock):
When there is a write operation, a blocked waiting for the completion of the write signal and a read operation. If no write operation is executed to read
Read unlock (RUnlock):
Reading unlock: Unlock read, read to complete the operation trigger
Write Lock (Lock):
After the first lock operation is performed, the blocking code, and then determine whether there is a read operation, when there is a read operation, wait, etc. after the completion of all the operations during a read operation of the remaining write operation is completed, the write the operation is complete
Write Unlock (Unlock):
First Departure write operation to complete, and then write unlocking
The above analysis of the code:
the first reading operation is performed twice, at this time, the read is not yet complete. Since concurrent, the write operation performed at this time, the code lock, waiting for the read operation is completed at this time, the number of read operations is 2, then perform SET (2) operation, since the lock, the code blocking this case, only the like set (1 ) unlock. Behind the read operation, due to pre-write operation, it is necessary to wait for the write operation to complete, due to the set (1) executed, before unlocking the first departure runtime_Semrelease, this time, to perform a read operation, the read operation is completed, the unlocking operation is performed, the remaining assignment operation performed
go get(1) //获取数据,read+1=1
time.Sleep(time.Millisecond * 1)
go get(2) //获取数据, read+1=2
time.Sleep(time.Millisecond * 1)
go set(1) //锁定 等待read完成:get(1)、get(2)
time.Sleep(time.Millisecond * 1)
go set(2) //代码锁定 等待set(1)完成
time.Sleep(time.Millisecond * 1)
go get(3) //获取数据,等待写操作完成即:set(1)完成,
time.Sleep(time.Millisecond * 1)
go set(3) //设置数据,等待set(2) 完成
time.Sleep(time.Millisecond * 1)
go set(4) //设置数据,等待set(2) 完成
time.Sleep(time.Millisecond * 1)
go get(4) //获取数据,等待写操作完成即:set(1)完成,
time.Sleep(time.Minute)