自定义互斥体 - 所有 goroutine 都在睡眠 - 死锁

问题描述 投票:0回答:1

我正在尝试制作一个具有特定行为的简单互斥体。但具体行为是不可能的,因为 golang 不会突然工作

简单的测试代码返回错误: 所有 goroutine 都在睡觉 - 僵局!

带有

for
的第一个块完美运行,没有错误 m.Unlock() 上的第二个块失败

怎么了?发生了什么事?我只是人类。我像人类一样编写代码。还有人类逻辑:

  1. 我用缓冲区打开了通道
  2. 我已将值发送到频道 我应该考虑哪些 go 例程?
Result:
=== RUN   TestNiceMutex_Lock
=== RUN   TestNiceMutex_Lock/test
   nicemutex_test.go:36: main: lock
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
testing.(*T).Run(0xc00007b380, {0xcee048?, 0xc1f6cd?}, 0xcf8388)
C:/Program Files/Go/src/testing/testing.go:1649 +0x3c8
testing.runTests.func1(0xdecd00?)
C:/Program Files/Go/src/testing/testing.go:2054 +0x3e
testing.tRunner(0xc00007b380, 0xc0000b7c48)
C:/Program Files/Go/src/testing/testing.go:1595 +0xff
testing.runTests(0xc00009a140?, {0xde0d90, 0x1, 0x1}, {0xbc3345?, 0xc0000b7d08?, 0x0?})
C:/Program Files/Go/src/testing/testing.go:2052 +0x445
testing.(*M).Run(0xc00009a140)
C:/Program Files/Go/src/testing/testing.go:1925 +0x636
main.main()
_testmain.go:47 +0x19c

goroutine 6 [chan receive]:
testing.(*T).Run(0xc00007b520, {0xceae57?, 0xc78420?}, 0xc00023c000)
C:/Program Files/Go/src/testing/testing.go:1649 +0x3c8
simply/nicemutex.TestNiceMutex_Lock(0x0?)
C:/Users/Aleksey/go/src/simply/nicemutex/nicemutex_test.go:26 +0xba
testing.tRunner(0xc00007b520, 0xcf8388)
C:/Program Files/Go/src/testing/testing.go:1595 +0xff
created by testing.(*T).Run in goroutine 1
C:/Program Files/Go/src/testing/testing.go:1648 +0x3ad

(content is repeated)

goroutine 97 [chan send]:
simply/nicemutex.lock()
C:/Users/Aleksey/go/src/simply/nicemutex/nicemutex.go:6
simply/nicemutex.TestNiceMutex_Lock.func1()
C:/Users/Aleksey/go/src/simply/nicemutex/nicemutex_test.go:12 +0x25
created by simply/nicemutex.TestNiceMutex_Lock in goroutine 6
C:/Users/Aleksey/go/src/simply/nicemutex/nicemutex_test.go:11 +0x2c


goroutine 117 [chan send]:
simply/nicemutex.(*NiceMutex).Lock(0xc000250008)
C:/Users/Aleksey/go/src/simply/nicemutex/nicemutex.go:25 +0x99
simply/nicemutex.TestNiceMutex_Lock.func2.1()
C:/Users/Aleksey/go/src/simply/nicemutex/nicemutex_test.go:32 +0x5f
created by simply/nicemutex.TestNiceMutex_Lock.func2 in goroutine 116
C:/Users/Aleksey/go/src/simply/nicemutex/nicemutex_test.go:30 +0xa5


Process finished with the exit code 1



package nicemutex

import (
    "sync"
    "testing"
    "time"
)

func TestNiceMutex_Lock(t *testing.T) {
    for i := 0; i < 100; i++ {
        go func() {
            lock()
            unlock()
        }()
        lock()
        unlock()
    }

    tests := []struct {
        name string
    }{
        {"test"},
    }
    wg := sync.WaitGroup{}
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            var m NiceMutex
            m.Lock()
            wg.Add(1)
            go func() {
                defer m.Unlock()
                m.Lock()
                t.Log("go: lock")
                wg.Done()
            }()
            t.Log("main: lock")
            time.Sleep(time.Second * 1)
            m.Unlock() // <- error
            wg.Wait()
        })
    }
}

package nicemutex

var internalLock = make(chan struct{}, 1)

func lock() {
    internalLock <- struct{}{} // <- error
}

func unlock() {
    <-internalLock
}

type NiceMutex struct {
    ch chan struct{}
}

func (m *NiceMutex) Lock() {
    defer unlock()
    lock()

    if m.ch == nil {
        m.ch = make(chan struct{}, 1)
    }
    m.ch <- struct{}{}
}

func (m *NiceMutex) Unlock() {
    defer unlock()
    lock() // <- error

    if m.ch == nil {
        return
    }
    <-m.ch
}
go parallel-processing runtime deadlock goroutine
1个回答
0
投票

欢迎来到SO!

首先,使用这样的通道作为互斥体并不完全“简单”。关于并发状态的推理很复杂。

但你的问题是你在

lock()
内再次调用
NiceMutex.Unlock
。该单缓冲区全局通道由
NiceMutex.Lock
填充并且永远不会被耗尽。

func (m *NiceMutex) Unlock() {
    // removed: defer unlock()
    unlock() // was: lock()

    if m.ch == nil {
        return
    }
    <-m.ch
}
© www.soinside.com 2019 - 2024. All rights reserved.