首页 > 解决方案 > 为什么互斥锁最终会出现线程正在休眠的错误?

问题描述

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    manager := NewPlatformManager()
    pTrain := &PassengerTrain{mediator:manager}
    mTrain := &MailTrain{mediator:manager}
    pTrain.Arrive()
    mTrain.Arrive()
    pTrain.Depart()
    //pTrain.Arrive()
    //mTrain.Depart()
    //time.Sleep(20000 * time.Millisecond)
}

/*
Mediator pattern
*/
type Train interface {
    Arrive()
    Depart()
    PermitArrival()
}

type Mediator interface {
    allowLanding(Train) bool
    notifyFree()
}

type PassengerTrain struct {
    mediator Mediator
}

func (t *PassengerTrain) Arrive() {
    if !t.mediator.allowLanding(t) {
        fmt.Println("Station Busy, passenger train")
    } else {
        fmt.Println("Landing on station, passenger train")
        time.Sleep(3000 * time.Millisecond)
    }
}

func (t *PassengerTrain) Depart() {
    t.mediator.notifyFree()
    fmt.Println("leaving station, passenger train")
}

func (t *PassengerTrain) PermitArrival() {
    fmt.Println("Allowed to arrive, passenger train")
    t.Arrive()
}

type MailTrain struct {
    mediator Mediator
}

func (t *MailTrain) Arrive() {
    if !t.mediator.allowLanding(t) {
        fmt.Println("Station Busy, mail train")
    } else {
        fmt.Println("Landing on station, mail train")
        time.Sleep(5000 * time.Millisecond)
    }
}

func (t *MailTrain) Depart() {
    t.mediator.notifyFree()
    fmt.Println("leaving station, mail train")
}

func (t *MailTrain) PermitArrival() {
    fmt.Println("Allowed to arrive, mail train")
    t.Arrive()
}

type PlatformManager struct {
    queue     []Train
    signal    *sync.Mutex
    isBlocked bool
}

func NewPlatformManager() Mediator {
    instance := &PlatformManager{
        queue:     make([]Train,0),
        signal:    &sync.Mutex{},
        isBlocked: false,
    }
    return instance
}

func (pm *PlatformManager) allowLanding(train Train) bool {
    pm.signal.Lock()
    defer pm.signal.Unlock()
    if !pm.isBlocked {
        pm.isBlocked = true
        return true
    }
    pm.queue = append(pm.queue, train)
    return false
}

func (pm *PlatformManager) notifyFree() {
    pm.signal.Lock()
    defer pm.signal.Unlock()

    if pm.isBlocked {
        pm.isBlocked = false
    }
    if len(pm.queue) > 0 {
        nextTrain := pm.queue[0]
        pm.queue = pm.queue[1:]
        nextTrain.PermitArrival()
    }
}

我收到如下错误,我期待代码进入单个流程,儿子也没有定义任何 go 例程。预期的结果应该是旅客列车到达(等待 3 秒)-> 邮件列车想要到达(被要求等待)-> 旅客列车离开 - > 邮件列车收到通知,它到达并等待(5 秒)

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_SemacquireMutex(0xc00002c00c, 0x486700, 0x1)
    /usr/local/go-faketime/src/runtime/sema.go:71 +0x47
sync.(*Mutex).lockSlow(0xc00002c008)
    /usr/local/go-faketime/src/sync/mutex.go:138 +0xfc
sync.(*Mutex).Lock(...)
    /usr/local/go-faketime/src/sync/mutex.go:81
main.(*PlatformManager).allowLanding(0xc000060150, 0x4dd220, 0xc000010210, 0x0)
    /tmp/sandbox325139098/prog.go:97 +0x17d
main.(*MailTrain).Arrive(0xc000010210)
    /tmp/sandbox325139098/prog.go:63 +0x48
main.(*MailTrain).PermitArrival(0xc000010210)
    /tmp/sandbox325139098/prog.go:78 +0x83
main.(*PlatformManager).notifyFree(0xc000060150)
    /tmp/sandbox325139098/prog.go:117 +0xbb
main.(*PassengerTrain).Depart(0xc000010200)
    /tmp/sandbox325139098/prog.go:49 +0x37
main.main()
    /tmp/sandbox325139098/prog.go:15 +0x13b

我在去操场上运行它https://play.golang.org/p/JO2mEQSk_kI

标签: gomutexsleep

解决方案


您收到此错误是因为func (pm *PlatformManager) notifyFree()创建了一个 lock onpm.signal然后调用nextTrain.PermitArrival()它又试图获得一个 lock on pm.signal。由于这种代码布局,nextTrain.PermitArrival()永远不会被锁定pm.signal,因此第 97 行出现错误。

您可以使用 goroutine 更优雅地实现这一点。您可以参考这个 ping pong示例开始。


推荐阅读