Golang如何保证goroutine中的数据在被另一个goroutine访问时完成

Dat*_*sik 0 go

嗨,大家好我正在尝试使用websockets创建一个派对系统,人们可以进入队列,然后匹配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")
}
Run Code Online (Sandbox Code Playgroud)

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

例如:p.PartialPartys说它没有派对.

我需要的是p.PartialPartys每个goroutine访问PartyHub结构域的每个人都是最新的我虽然sync.Mutex会为我做这个但是似乎不是这样,有人能告诉我最好的方法来保留我所有的goroutines与同一数据同步?

Jam*_*dge 5

因此,通过这种实现,您的五个goroutine都不能并行运行,因为它们都试图释放p.Mu互斥锁.看看你使用p.AddPartialParty频道的方式,如果代码可以死锁,我也不会感到惊讶.

考虑以下事件序列:

  1. 其中一个SortMemberIntoPartygoroutines开始运行并获得互斥锁.
  2. 它发送一个值p.AddPartialParty,由...接收Run. Run然后试图获取互斥锁,所以阻止.
  3. 原始SortMemberIntoPartygoroutine完成并释放互斥锁.
  4. 另一个SortMemberIntoPartygoroutine获取互斥锁,并尝试发送另一个值p.AddPartialParty.
  5. goroutine阻塞因为没有人准备读取值(Run在返回select语句之前仍在等待互斥锁).

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

如果你确实需要互斥锁,那么直接进行SortMemberIntoPartygoroutine更新可能p.PartialPartys比使用频道更容易:你已经知道没有其他人会同时访问变量.

值得记住的是,这个互斥锁本质上意味着所有SortMemberIntoPartygoroutine都将被序列化.如果你正在使用goroutines以期在这里实现并行性,那么互斥体就会失败.