【问题标题】:Go test hangs when using Mutex Lock and Unlock使用互斥锁和解锁时 Go 测试挂起
【发布时间】:2021-06-21 08:43:21
【问题描述】:

我是 GO 的新手,我不得不在我的代码中使用互斥锁/解锁来防止并发访问。但是在我将锁定/解锁添加到我的代码之后,我的测试开始永远运行。我简化了我的用例并添加了类及其测试文件。如果我单独运行测试,一切运行良好。但是如果我运行整个文件,前两个就完成了,但第三个永远运行。如果我删除锁定/解锁,则运行正确执行。

如果有人能指出我在这里做错了什么,那就太好了。

被测代码:

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)
}

【问题讨论】:

    标签: unit-testing go synchronization mutex


    【解决方案1】:

    如果Cache.Get() 方法找到该项目,它会返回而不调用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 的访问就足够了被访问)。嵌入互斥锁也是一个好主意,见When do you embed mutex in struct in Go?

    【讨论】:

    • 愚蠢的错误:(完全忘记了return语句。感谢指出defer的用法。
    猜你喜欢
    • 2018-05-23
    • 2016-08-04
    • 1970-01-01
    • 2016-05-19
    • 2021-02-03
    • 2022-07-31
    • 2010-12-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多