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

Datsik:

嗨,大家好,我正在尝试使用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说没有聚会。

我需要的是p.PartialPartys始终对goroutine访问该PartyHub结构的该字段的每个对象保持最新状态,尽管这sync.Mutex会为我做到这一点,但事实并非如此,有人可以告诉我保留所有goroutine的最佳方法吗?与相同数据同步?

詹姆斯·亨斯特里奇(James Henstridge):

因此,使用此实现,您的五个goroutine都将无法并行运行,因为它们都试图使p.Mu互斥体无罪再看看您使用p.AddPartialParty通道的方式,如果代码可能出现死锁,我也不会感到惊讶。

请考虑以下事件序列:

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

因此,现在您有了一个阻塞的goroutine,该例程持有通道的接收端所需的锁。另请注意,在(4)处您将看不到新内容,PartialParty因为Run尚未设法添加它。

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

还值得记住的是,这个互斥锁本质上意味着所有SortMemberIntoPartygoroutine将被序列化。如果您使用goroutines希望在这里实现并行性,则互斥锁会克服这一点。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

从调用另一个goroutine的goroutine返回

从goroutine获得价值并取消另一个goroutine

如何通知另一个goroutine停止?

无法在一个goroutine中写入并不确定地从另一个goroutine中读取

将项目添加到一个 goroutine 中的通道并在另一个 goroutine 中处理

一个goroutine从结构中读取而另一个goroutine正在对其进行修改是否安全?

Webhook进程在另一个goroutine上运行

使用来自另一个数据集的信息完成数据集

如何等待第一个完成的goroutine

当第一个完成时如何安全绕开其他goroutine的结果

如何使多个go例程等待另一个goroutine的某些输出

如何关闭另一个goroutine的读取TCP连接?

如何判断一个 goroutine 是否成功或所有 goroutine 都已完成?

等待一个goroutine完成

如何从另一个数据类中的数据类访问数据?

如何在Golang中访问另一个包的私有功能?

如何在另一个Struct中访问golang Struct数组?

一个完成后如何完成所有goroutine

我如何从另一个集团访问数据

在 C# 中触发另一个事件时,如何防止一个事件方法完成?

仅在测试时,如何允许一个包访问另一个包的未导出数据?

在为多个 goroutine 使用单个 http 客户端时,在一个线程上编辑传输会影响另一个线程吗?

如何在React组件中的另一个函数中访问useEffect的异步数据

访问另一个组件中的缓存时如何从 useQuery 中提取单个项目?

从另一个文件调用函数时如何访问 ViewController 中的 UIView?

如何在python中从一个日期到另一个日期访问csv中的数据

如何从Flutter中的另一个类访问全局数据?

如何从JasperReports中的另一个数据源(JSON)访问字段的值?

如何在python数据类的默认参数中访问另一个参数?