Golang我如何保证数据在一个goroutine中完成,同时被另一个gorroutine访问



嗨,伙计们,我正在尝试使用websocket创建一个聚会系统,人们可以进入队列,然后与5个类似的人进行匹配。现在我在这个部分遇到了麻烦:

type PartyHub struct {
    Partys             map[string]*Party
    PartialPartys      []*PartialParty
    Queue              []*Member
    AddParty           chan *Party
    RemoveParty        chan *Party
    AddPartialParty    chan *PartialParty
    RemovePartialParty chan *PartialParty
    EnterQueue         chan *Member
    LeaveQueue         chan *Member
    Mu                 sync.Mutex
}
// Run will begin monitoring the channels
// to register and unregister partys as they are
// created or destroyed
func (p *PartyHub) Run() {
    for {
        select {
        case member := <-p.EnterQueue:
            go p.SortMemberIntoParty(member)
            go p.SortMemberIntoParty(member)
            go p.SortMemberIntoParty(member)
            go p.SortMemberIntoParty(member)
            go p.SortMemberIntoParty(member)
            log.Println(p.PartialPartys)
        case party := <-p.AddPartialParty:
            p.Mu.Lock()
            defer p.Mu.Unlock()
            p.PartialPartys = append(p.PartialPartys, party)
        }
    }
}
// SortMemberIntoParty will take a new user entering the queue and find an appropriate Party
// for the member to join, taking into account RankTollerance, Rank
func (p *PartyHub) SortMemberIntoParty(member *Member) {
    p.Mu.Lock()
    defer p.Mu.Unlock()
    if len(p.PartialPartys) == 0 {
        log.Println("Here")
        newParty := &PartialParty{Accepting: true, Members: []*Member{member}}
        p.AddPartialParty <- newParty
        return
    }
    foundPartyForMember := false
    for _, party := range p.PartialPartys {
        goodFitForParty := true
        for _, partyMember := range party.Members {
            log.Println(member.Type == partyMember.Type, member.Rank >= partyMember.Rank-partyMember.RankTol, member.Rank <= partyMember.Rank+partyMember.RankTol)
            if member.Type == partyMember.Type && member.Rank >= partyMember.Rank-partyMember.RankTol && member.Rank <= partyMember.Rank+partyMember.RankTol {
                goodFitForParty = true
                continue
            } else {
                goodFitForParty = false
                break
            }
        }
        if !goodFitForParty {
            continue
        } else {
            foundPartyForMember = true
            party.Mu.Lock()
            defer party.Mu.Unlock()
            party.Members = append(party.Members, member)
            if len(party.Members) == 5 {
                party.Accepting = false
                go party.SendReadyCheck()
            }
            break
        }
    }

    if !foundPartyForMember {
        newParty := &PartialParty{Accepting: true, Members: []*Member{member}}
        p.AddPartialParty <- newParty
    }
    log.Println("Sorting Members")
}

唯一的问题是,5 goroutines似乎比数据知道发生了什么更快地完成。

例如:p.PartialPartys说它没有政党。

我需要的是让访问PartyHub结构的该字段的每个goroutinep.PartialPartys始终是最新的。虽然sync.Mutex会为我这样做,但事实似乎并非如此,有人能告诉我保持所有goroutine与相同数据同步的最佳方法吗?

因此,有了这个实现,五个goroutine中没有一个能够并行运行,因为它们都试图获取p.Mu互斥。看看您使用p.AddPartialParty通道的方式,如果代码会死锁,我也不会感到惊讶。

考虑以下事件序列:

  1. 其中一个SortMemberIntoParty goroutine开始运行并获取互斥
  2. 它在p.AddPartialParty上发送一个值,该值由Run接收。Run然后尝试获取互斥,因此阻止
  3. 原始的SortMemberIntoParty goroutine完成并释放互斥体
  4. 另一个SortMemberIntoParty goroutine获取互斥,并尝试向p.AddPartialParty发送另一个值
  5. goroutine会阻塞,因为没有人准备好读取该值(Run在返回到select语句之前仍在等待互斥对象)

所以现在你有一个被阻塞的goroutine,它持有通道接收端所需的锁。还要注意,在(4)中,您不会看到新的PartialParty,因为Run还没有设法添加它。

如果您确实需要互斥,那么直接让SortMemberIntoParty goroutine更新p.PartialPartys可能比使用通道更容易:您已经知道没有其他人会同时访问该变量。

同样值得记住的是,这个互斥锁本质上意味着所有SortMemberIntoParty goroutine都将串行化。如果您使用goroutines是希望在这里实现并行性,那么互斥对象就无法做到这一点。

最新更新