无法弄清楚为什么这个循环是数据竞争

时间:2015-12-03 09:07:28

标签: go

我有一个循环,显然导致数据竞争接近这个功能的底部,我将标记:

func (p *PartialParty) SendReadyCheck(party PartialParty) {
    msg, err := json.Marshal(&ReadyCheckMsg{"ReadyCheck", ""})
    if err != nil {
        log.Println(err)
    }

    for _, member := range party.Members {
        member.Conn.send <- msg
    }

    counter := 0
    loopBreaker := true
    for {
        select {
        case <-p.Accept:
            counter++
            resp, err := json.Marshal(&ReadyCheckMsg{"ReadyAccepted", ""})
            if err != nil {
                log.Println(err)
            }
            for _, member := range party.Members {
                member.Conn.send <- resp
            }
            if counter == 2 {
                // Create a new party with all members
                partyid := PartyID(feeds.NewUUID().String())
                db := common.Db()
                newParty := &Party{
                    Active:    true,
                    Members:   p.Members,
                    Broadcast: make(chan []byte),
                    PartyID:   partyid,
                }

                // Insert the new party into the database
                _, err := db.Exec("INSERT INTO party SET party_id = ?, active = ?", partyid.String(), true)
                if err != nil {
                    log.Println(err)
                }

                // Go through the members and update the database
                var wg sync.WaitGroup

                for _, member := range party.Members {
                    wg.Add(1)
                    m := member
                    go func() {
                        _, err := db.Exec("UPDATE party_members SET active = ? WHERE steamid = ?", false, m.SteamID)
                        if err != nil {
                            log.Println(err)
                        }
                        _, err = db.Exec("INSERT INTO party_members SET belongs_to =?, active = ?, steamid = ?", partyid.String(), true, m.SteamID)
                        if err != nil {
                            log.Println(err)
                        }
                        wg.Done()
                    }()
                }

                // Wait for all the database stuff to finish
                wg.Wait()
                PHub.AddNewParty(newParty)
                loopBreaker = false
                break
            }
        case conn := <-p.Decline:
            if conn.Ready {
                break
            }
            conn.Ready = false
            conn.InQueue = false
            conn.CurrentParty = ""
            resp, err := json.Marshal(&ReadyCheckMsg{"ReadyCheckDeclined", ""})
            if err != nil {
                log.Println(err)
            }
            p.Accepting = true
            identifier := conn.Identifier
            if _, ok := party.Members[identifier]; ok {
                delete(p.Members, identifier)
            }
            for _, m := range party.Members {
                member := m
                member.Conn.send <- resp
            }
            log.Println("Here")
            loopBreaker = false
            break
        case <-time.After(30 * time.Second):
            if counter == 2 {
                return
            }
            p.Accepting = true
            failedMsg, err := json.Marshal(&ReadyCheckMsg{"FailedToReady", ""})
            if err != nil {
                log.Println(err)
            }
            somebodyDeclinedMsg, err := json.Marshal(&ReadyCheckMsg{"ReadyCheckDeclined", ""})
            if err != nil {
                log.Println(err)
            }


    >>>>     for _, member := range party.Members { ***<<<< This Line***
                m := member
                if !m.Conn.Ready {
                    m.Conn.Ready = false
                    m.Conn.InQueue = false
                    m.Conn.CurrentParty = ""
                    m.Conn.send <- failedMsg
                } else {
                    m.Conn.Ready = false
                    m.Conn.send <- somebodyDeclinedMsg
                }
            }
            loopBreaker = false
            break
        }
        if !loopBreaker {
            break
        }
    }
}

显然与此相矛盾:

// AddNewMember will add a new user to the party
func (p *PartyHub) AddNewMember(member *Member, partyid PartyID) {
    p.Lock()
    defer p.Unlock()
>>> p.PartialPartys[partyid].Members[member.Conn.Identifier] = member
}

type PartialParty struct {
    Accepting bool
    Members   map[Identifier]*Member
    Accept    chan *Connection
    Decline   chan *Connection
    PartyID   PartyID
    sync.Mutex
}

现在,如果goroutine AddNewMember正在运行,则SendReadyCheck是不可能的,因为它受if语句保护,该语句检查goroutine是否正在运行,所以我不确定为什么他们说他们互相竞争。任何有关清理它的帮助都会很棒。我已经尝试在循环中设置一个变量来试图摆脱它但它似乎并没有引起它

1 个答案:

答案 0 :(得分:0)

  

现在,如果goroutine SendReadyCheck正在运行,那么AddNewMember是不可能的,因为它受if语句的保护,该语句检查goroutine是否正在运行

你并没有真正展示代码的一部分,但可能并非如此。如果SendReadyCheck在 if测试之后但在AddNewMember进行修改之前开始运行会怎样?