Go 测试在使用 Mutex Lock 和 Unlock 时挂起
Go test hangs when using Mutex Lock and Unlock
我是 GO 的新手,我不得不在我的代码中使用 Mutex Lock/Unlock 来防止并发访问。但是在我将 Lock/Unlock 添加到我的代码后,我的测试开始永远 运行 。我简化了我的用例并添加了 class 及其测试文件。如果我 运行 单独测试,所有 运行 没问题。但是如果我 运行 整个文件,前两个完成,但第三个 运行 永远。如果我删除 Lock/Unlock,那么 运行 会正确执行。
如果有人能指出我在这里做错了什么,那就太好了。
被测代码:
package anything
import (
"sync"
)
var maxLimit = 5
var Store = Cache{make([]string, 0)}
var mutex = new(sync.RWMutex)
type Cache struct {
items []string
}
func (cache *Cache) Push(item string) {
mutex.Lock()
if len(cache.items) == maxLimit {
cache.items = append(cache.items[:0], cache.items[1:]...)
}
cache.items = append(cache.items, item)
mutex.Unlock()
}
func (cache *Cache) Get(content string) string {
mutex.RLock()
for _, item := range cache.items {
if item == content {
return content
}
}
mutex.RUnlock()
return ""
}
测试文件:
package anything
import (
"github.com/stretchr/testify/assert"
"strconv"
"testing"
)
func TestPush_MoreThanTheMaxLimit_RemoveFirstItem(t *testing.T) {
for i := 0; i <= maxLimit; i++ {
item := strconv.Itoa(i)
Store.Push(item)
}
var actual = Store.items[0]
assert.Equal(t, "1", actual)
}
func TestGet_PresentInCache_ReturnsItem(t *testing.T) {
Store.Push(strconv.Itoa(1))
Store.Push(strconv.Itoa(3))
var actual = Store.Get("1")
assert.Equal(t, "1", actual)
}
func TestGet_NotPresentInCache_ReturnsNil(t *testing.T) {
Store.Push(strconv.Itoa(1))
Store.Push(strconv.Itoa(3))
var actual = Store.Get("7")
assert.Empty(t, actual)
}
如果 Cache.Get()
方法找到该项目,它会 returns 而不调用 mutex.RUnlock()
,因此互斥量保持锁定状态。然后再次调用 Cache.Get()
将阻塞,因为您无法锁定已锁定的互斥量。
改为使用 defer
解锁,因此无论函数如何结束,互斥锁都会被解锁:
func (cache *Cache) Get(content string) string {
mutex.RLock()
defer mutex.RUnlock()
for _, item := range cache.items {
if item == content {
return content
}
}
return ""
}
还可以考虑将互斥锁添加到 Cache
结构本身,因为该互斥锁应该保护对 Cache
值的并发访问。在您的示例中很好,但是如果您要创建 Cache
的多个值,单个互斥量将是不够的(会不必要地阻止对所有 Cache
值的访问,而足以阻止对单个 Cache
被访问)。嵌入互斥锁也是一个好主意,请参阅
我是 GO 的新手,我不得不在我的代码中使用 Mutex Lock/Unlock 来防止并发访问。但是在我将 Lock/Unlock 添加到我的代码后,我的测试开始永远 运行 。我简化了我的用例并添加了 class 及其测试文件。如果我 运行 单独测试,所有 运行 没问题。但是如果我 运行 整个文件,前两个完成,但第三个 运行 永远。如果我删除 Lock/Unlock,那么 运行 会正确执行。
如果有人能指出我在这里做错了什么,那就太好了。
被测代码:
package anything
import (
"sync"
)
var maxLimit = 5
var Store = Cache{make([]string, 0)}
var mutex = new(sync.RWMutex)
type Cache struct {
items []string
}
func (cache *Cache) Push(item string) {
mutex.Lock()
if len(cache.items) == maxLimit {
cache.items = append(cache.items[:0], cache.items[1:]...)
}
cache.items = append(cache.items, item)
mutex.Unlock()
}
func (cache *Cache) Get(content string) string {
mutex.RLock()
for _, item := range cache.items {
if item == content {
return content
}
}
mutex.RUnlock()
return ""
}
测试文件:
package anything
import (
"github.com/stretchr/testify/assert"
"strconv"
"testing"
)
func TestPush_MoreThanTheMaxLimit_RemoveFirstItem(t *testing.T) {
for i := 0; i <= maxLimit; i++ {
item := strconv.Itoa(i)
Store.Push(item)
}
var actual = Store.items[0]
assert.Equal(t, "1", actual)
}
func TestGet_PresentInCache_ReturnsItem(t *testing.T) {
Store.Push(strconv.Itoa(1))
Store.Push(strconv.Itoa(3))
var actual = Store.Get("1")
assert.Equal(t, "1", actual)
}
func TestGet_NotPresentInCache_ReturnsNil(t *testing.T) {
Store.Push(strconv.Itoa(1))
Store.Push(strconv.Itoa(3))
var actual = Store.Get("7")
assert.Empty(t, actual)
}
如果 Cache.Get()
方法找到该项目,它会 returns 而不调用 mutex.RUnlock()
,因此互斥量保持锁定状态。然后再次调用 Cache.Get()
将阻塞,因为您无法锁定已锁定的互斥量。
改为使用 defer
解锁,因此无论函数如何结束,互斥锁都会被解锁:
func (cache *Cache) Get(content string) string {
mutex.RLock()
defer mutex.RUnlock()
for _, item := range cache.items {
if item == content {
return content
}
}
return ""
}
还可以考虑将互斥锁添加到 Cache
结构本身,因为该互斥锁应该保护对 Cache
值的并发访问。在您的示例中很好,但是如果您要创建 Cache
的多个值,单个互斥量将是不够的(会不必要地阻止对所有 Cache
值的访问,而足以阻止对单个 Cache
被访问)。嵌入互斥锁也是一个好主意,请参阅