golang the Mutex and RWMutex

    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)

Guess you like

Origin www.cnblogs.com/zp900704/p/11004858.html