在同一个 go routine 中使用 Lock 和 Rlock
Using Lock and Rlock in the same go routine
我正在使用 Go 中的 RWMutex 进行实验,我意识到可以使用以下代码实现此行为:
- 协程 1 - RLock
- 协程 1 - RUnlock
- goroutine 2 - RLock
- goroutine 2 - RUnlock
- goroutine 2 - 锁
- goroutine 2 - 解锁
- goroutine 1 - 锁
- goroutine 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("%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来读和Lock来写。但是,正如你在上面的代码中看到的,如果两个 Goroutines 几乎同时启动,则有可能在同一个映射中有两次写入,而不是一次写入和一次读取。
由此,我有一些问题:
- 有没有一种方法可以保证只有一个 goroutine 写入映射(输入上面的 if 代码块),而所有其他例程使用新值读取该映射(避免输入 if 代码块)?
- 它是 goroutine 和 maps 的正确实现吗?
这是因为您的代码中存在竞争条件。您对映射进行读锁定以读取它、做出决定,然后对其进行写锁定。无法保证当您获得写锁时,您做出决定的条件仍然成立。
正确的方法是在锁定后重新测试条件:
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
的使用,否则它必须再次访问地图。
我正在使用 Go 中的 RWMutex 进行实验,我意识到可以使用以下代码实现此行为:
- 协程 1 - RLock
- 协程 1 - RUnlock
- goroutine 2 - RLock
- goroutine 2 - RUnlock
- goroutine 2 - 锁
- goroutine 2 - 解锁
- goroutine 1 - 锁
- goroutine 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("%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来读和Lock来写。但是,正如你在上面的代码中看到的,如果两个 Goroutines 几乎同时启动,则有可能在同一个映射中有两次写入,而不是一次写入和一次读取。
由此,我有一些问题:
- 有没有一种方法可以保证只有一个 goroutine 写入映射(输入上面的 if 代码块),而所有其他例程使用新值读取该映射(避免输入 if 代码块)?
- 它是 goroutine 和 maps 的正确实现吗?
这是因为您的代码中存在竞争条件。您对映射进行读锁定以读取它、做出决定,然后对其进行写锁定。无法保证当您获得写锁时,您做出决定的条件仍然成立。
正确的方法是在锁定后重新测试条件:
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
的使用,否则它必须再次访问地图。