大家好,我正在尝试使用 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")
}
唯一的问题是,5 个 goroutines
似乎比数据知道发生了什么更快完成。
例如:p.PartialPartys
表示它没有派对。
我需要的是让 p.PartialPartys
始终为每个访问 PartyHub
结构字段的 goroutine
保持最新sync.Mutex
会为我做这件事,但似乎并非如此,有人能告诉我让我的所有 goroutine 与相同数据保持同步的最佳方法吗?
最佳答案
因此,通过此实现,您的五个 goroutine 都无法并行运行,因为它们都在尝试获取 p.Mu
互斥量。看看您使用 p.AddPartialParty
channel 的方式,如果代码会死锁,我不会感到惊讶。
考虑以下事件序列:
- 其中一个
SortMemberIntoParty
goroutine 开始运行并获取互斥量。 - 它在
p.AddPartialParty
上发送一个值,该值由Run
接收。Run
然后尝试获取互斥锁,因此阻塞。 - 原始
SortMemberIntoParty
goroutine 完成并释放互斥体。 - 不同的
SortMemberIntoParty
goroutine 获取互斥量,并尝试将另一个值发送到p.AddPartialParty
。 - goroutine 阻塞是因为没有人准备好读取值(
Run
在返回到select
语句之前仍在等待互斥量)。<
现在您已经有了一个阻塞的 goroutine,它持有 channel 接收端所需的锁。另请注意,在 (4) 处您不会看到新的 PartialParty
,因为 Run
尚未设法添加它。
如果您确实需要互斥量,那么直接让您的 SortMemberIntoParty
goroutine 更新 p.PartialPartys
比使用 channel 更容易:您已经知道没有其他人会同时访问该变量。
同样值得记住的是,这个互斥锁本质上意味着所有 SortMemberIntoParty
goroutines 都将被序列化。如果您使用 goroutines 是希望在这里实现并行性,那么互斥量会打败它。
关于Golang 如何保证数据在一个 goroutine 中完成,同时被另一个 goroutine 访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34011131/