Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
141 lines (109 sloc) 4.7 KB

sync.RWMutex类型

sync.RWMutex 类型是另外一种互斥体,它是 sync.Mutex 的改进版,在 sync 目录的 rwmutex.go 文件中定义如下:

type RWMutex struct {
    w           Mutex
    writerSem   uint32
    readerSem   uint32
    readerCount int32
    readerWait  int32
}

换句话说,sync.RWMutex 是基于 sync.Mutex 做了必要的添加和改进。

现在让我们来说说 sync.RWMutex 是如果改进 sync.Mutex 的。尽管使用 sync.RWMutex 互斥锁只允许有一个函数执行写操作,但您能有多个属于 sync.RWMutex 互斥锁的读取者。然而,有一件事您要注意:除非sync.RWMutex 互斥锁的所有读取者解锁,您不能为写操作给它上锁,这也是为了实现多互斥锁读取而付出的代价。

RLock()RUnlock() 是出于读取的目的帮您与 sync.RWMutex 互斥体工作的函数,它们分别用于上锁和解锁互斥体。当您为了写操作想要上锁和解锁一个 sync.RWMutex 互斥体时,用在 sync.Mutex 互斥体的 Lock()Unlock() 函数仍适用。因此为了读操作 RLock() 函数应该与 RUnlock() 配对调用。最后,显而易见,您不应该修改在 RLock()RUnlock() 代码块间的任何共享变量。

rwMutex.go 代码说明了 sync.RWMutex 类型的使用和通途。程序分六部分介绍,并且相同的函数有俩个稍有不同的版本。第一个为读操作使用 sync.RWMutex 互斥体,第二个为读操作使用 sync.Mutex 互斥体。这两个函数之间的不同表现会帮助您理解为了读取操作使用 sync.RWMutex 互斥体的好处更多。

rwMutex.go 的第一部分如下:

package main

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

var Password = secret{password: "myPassword"}

type secret struct {
    RWM         sync.RWMutex
    M           sync.Mutex
    password    string
}

secret 结构有一个共享变量,一个 sync.RWMutex 互斥锁和一个 sync.Mutex 互斥锁。

rwMutex.go 的第二段代码如下:

func Change(c *secret, pass string) {
    c.RWM.Lock()
    fmt.Println("LChange")
    time.Sleep(10 * time.Second)
    c.password = pass
    c.RWM.Unlock()
}

Change() 函数修改共享变量,意味着您需要使用一个排他锁,这就是使用 Lock()Unlock() 函数的原因。当做变更时离不开使用排他锁!

rwMutex.go 的第三部分如下:

func show(c *secret) string {
    c.RWM.RLock()
    fmt.Println("show")
    time.Sleep(3 * time.Second)
    defer c.RWM.RUnlock()
    return c.password
}

show() 函数使用 RLock()RUnlock() 函数是因为它的关键部分是用来读取共享变量的。因此,尽管 goroutines 能够读取共享变量,但是在不使用 Lock()Unlock() 函数的情况下,所有 goroutines 都不能修改共享变量。但是,只要有人使用互斥锁读取共享变量,Lock() 函数就会被一直阻塞。

rwMutex.go 的第四段代码如下:

func showWithLock(c *secret) string {
    c.M.Lock()
    fmt.Println("showWithLock")
    time.Sleep(3 * time.Second)
    defer c.M.Unlock()
    return c.password
}

showWithLock() 函数和 show() 函数之间唯一的不同是 showWithLock() 函数为了读操作使用了排他锁,这意味着只有一个 showWithLock() 函数能读取 secret 结构体的 password 字段。

rwMutex.go 的第五段代码如下:

func main() {
    var showFunction = func(c *secret) string {return ""}
    if len(os.Args) != 2 {
        fmt.Println("Using sync.RWMutex!")
        showFunction = show
    } else {
        fmt.Println("Using sync.Mutex!")
        showFunction = showWithLock
    }

    var waitGroup sync.WaitGroup
    fmt.Println("Pass:", showFunction(&Password))

rwMutex.go 的其余代码如下:

    for i := 0 ; i < 15; i++ {
        waitGroup.Add(1)
        go func() {
            defer waitGroup.Done()
            fmt.Println("Go Pass:", showFunction(&Password))
        }()

        go func(){
            waitGroup.Add(1)
            defer waitGroup.Done()
            Change(&Password, "123456")
        }()
        waitGroup.Wait()
        fmt.Println("Pass:", showFunction(&Password))
    }
}

执行 rwMutex.go 两次并使用 time(1) 命令行工具测试这个程序的两个版本将产生如下输出:

$time go run rwMutex.go 10 >/dev/null

real 0m51.206s
user 0m0.130s
sys  0m0.074s
$time go run rwMutex.go >/dev/null

real 0m22.191s
user 0m0.135s
sys  0m0.071s

注意上面命令结尾处的 > /dev/null 是为了忽略这两个命令的输出。因此,使用 sync.RWMutex 互斥体的版本要比使用 sync.Mutex 的版本快很多。

You can’t perform that action at this time.