在同一个go例程中使用Lock和Rlock



我正在Go中使用RWMutex进行实验,并且我意识到可以使用以下代码实现此行为:

  • 程序1 - RLock
  • 运行程序1 -运行锁
  • 程序2 - RLock
  • 运行程序2 -运行锁
  • 程序2 -锁
  • 程序2 -解锁
  • 程序1 -锁定
  • 程序1 -解锁
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("%sRLockn", goroutine)
t := r.mapEx[value]
r.mu.RUnlock()
fmt.Printf("%sRUnlockn", goroutine)
if len(t) <= 0 {
time.Sleep(500 * time.Millisecond)
r.mu.Lock()
fmt.Printf("%sLockn", goroutine)
r.mapEx[value] = value
r.mu.Unlock()
fmt.Printf("%sUnlockn", goroutine)
return r.mapEx[value]
}
return t
}

我在一些文章中读到,在例程中使用map的正确方法是使用RWMutex和RLock来读和Lock来写。然而,正如您在上面的代码中看到的,如果两个goroutine几乎在同一时间启动,则可能在同一个映射中有两个写操作,而不是一个写操作和一个读操作。

由此,我有一些问题:

  1. 是否有一种方法,我们可以保证只有一个例程写入映射(进入上面的if代码块)和所有其他例程读取映射的新值(避免进入if代码块)?
  2. 是正确的实现程序和地图?

这是因为您的代码中有一个竞争条件。通过读锁来读取映射,做出决定,然后再写锁。当您获得写锁时,不能保证您所做决定的条件仍然有效。

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

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

注意上面使用的return value,否则它将不得不再次访问映射。

最新更新