嗨,伙计们,我正在尝试使用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
结构的该字段的每个goroutine
的p.PartialPartys
始终是最新的。虽然sync.Mutex
会为我这样做,但事实似乎并非如此,有人能告诉我保持所有goroutine与相同数据同步的最佳方法吗?
因此,有了这个实现,五个goroutine中没有一个能够并行运行,因为它们都试图获取p.Mu
互斥。看看您使用p.AddPartialParty
通道的方式,如果代码会死锁,我也不会感到惊讶。
考虑以下事件序列:
- 其中一个
SortMemberIntoParty
goroutine开始运行并获取互斥 - 它在
p.AddPartialParty
上发送一个值,该值由Run
接收。Run
然后尝试获取互斥,因此阻止 - 原始的
SortMemberIntoParty
goroutine完成并释放互斥体 - 另一个
SortMemberIntoParty
goroutine获取互斥,并尝试向p.AddPartialParty
发送另一个值 - goroutine会阻塞,因为没有人准备好读取该值(
Run
在返回到select
语句之前仍在等待互斥对象)
所以现在你有一个被阻塞的goroutine,它持有通道接收端所需的锁。还要注意,在(4)中,您不会看到新的PartialParty
,因为Run
还没有设法添加它。
如果您确实需要互斥,那么直接让SortMemberIntoParty
goroutine更新p.PartialPartys
可能比使用通道更容易:您已经知道没有其他人会同时访问该变量。
同样值得记住的是,这个互斥锁本质上意味着所有SortMemberIntoParty
goroutine都将串行化。如果您使用goroutines是希望在这里实现并行性,那么互斥对象就无法做到这一点。