首页 > 解决方案 > 在同一个 goroutine 中使用 Lock 和 Rlock

问题描述

我正在使用 Go 中的 RWMutex 进行实验,我意识到使用以下代码可能会出现这种行为:

package main

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

type RLockAndLockStruct struct {
    mu sync.RWMutex

    mapEx map[string]string
}

func main() {
    r := &RLockAndLockStruct{}
    r.mapEx = make(map[string]string)

    go r.RLockAndLockTest("test", "goroutine 1 - ")
    go r.RLockAndLockTest("test", "goroutine 2 - ")
    time.Sleep(4000 * time.Millisecond)
}

func (r *RLockAndLockStruct) RLockAndLockTest(value string, goroutine string) string {
    r.mu.RLock()
    fmt.Printf("%sRLock\n", goroutine)
    t := r.mapEx[value]
    r.mu.RUnlock()
    fmt.Printf("%sRUnlock\n", goroutine)
    if len(t) <= 0 {
        time.Sleep(500 * time.Millisecond)
        r.mu.Lock()
        fmt.Printf("%sLock\n", goroutine)
        r.mapEx[value] = value
        r.mu.Unlock()
        fmt.Printf("%sUnlock\n", goroutine)
        return r.mapEx[value]
    }
    return t
}

我在一些文章中读到,在 Goroutines 中使用 map 的正确方法是使用 RWMutex 和 RLock 来读取和锁定来写入。但是,正如您在上面的代码中看到的那样,如果两个 Goroutine 几乎同时启动,则可能在同一个 map 中有两次写入,而不是一次写入和一次读取。

由此,我在这里有一些问题:

  1. 有没有一种方法可以保证只有一个 goroutine 在 map 上写入(输入上面的 if 代码块),而所有其他例程都使用新值读取该 map(避免输入 if 代码块)?
  2. 它是 goroutine 和 maps 的正确实现吗?

标签: gogoroutine

解决方案


这是因为您的代码中有竞争条件。你读锁地图来阅读它,做出决定,然后写锁它。无法保证当您获得写锁时,您做出决定的条件仍然成立。

正确的方法是在锁定后重新测试条件:

    if len(t) <= 0 {
        time.Sleep(500 * time.Millisecond)
        r.mu.Lock()
        if len(r.mapEx[value])<=0 {
          fmt.Printf("%sLock\n", goroutine)
          r.mapEx[value] = value
        }
        r.mu.Unlock()
        return value

注意return value上面的使用,否则它必须再次访问地图。


推荐阅读