我怎样才能摆脱这种数据竞争

时间:2015-12-26 06:28:27

标签: go

我有这两个功能:

// PartyHub struct contains all data for the party
type PartyHub struct {
    FullPartys    map[string]Party
    PartialPartys map[string]Party
    Enter         chan Member
    Leave         chan Member
    sync.Mutex
}


// RemoveFromQueue will remove the member from party
func (p *PartyHub) RemoveFromQueue(memberLeaving Member, inQueue bool) {
    if !inQueue {
        return
    }
    for _, party := range p.PartialPartys {
        go func(party Party) {
            if _, ok := party.Members[memberLeaving.Identifier]; ok {
                p.Lock()
->>>>>>>>       delete(party.Members, memberLeaving.Identifier)
                p.Unlock()
            }
        }(party)
    }
    log.Println("Removing")
}

// SortIntoParty will sort the member into party
func (p *PartyHub) SortIntoParty(newMember Member, inQueue bool) {
    log.Println(inQueue)
    if inQueue {
        return
    }
    log.Println("Adding")
    foundParty := false
->> for partyid, party := range p.PartialPartys {
        if !party.Accepting {
            continue
        }

        goodFitForParty := true
        for _, partyMember := range party.Members {
            if newMember.Type == partyMember.Type && newMember.Rank >= partyMember.Rank-partyMember.RankTol && newMember.Rank <= partyMember.Rank+partyMember.RankTol {
                goodFitForParty = true
                continue
            } else {
                goodFitForParty = false
                break
            }
        }

        if !goodFitForParty {
            continue
        } else {
            foundParty = true
            newMember.Conn.CurrentParty = partyid
            p.Lock()
            p.PartialPartys[partyid].Members[newMember.Conn.Identifier] = newMember
            p.Unlock()
            if len(party.Members) == 2 {
                p.Lock()
                party.Accepting = false
                p.Unlock()
                // Start Go Routine
            }
            break
        }
    }
    if !foundParty {
        uuid := feeds.NewUUID().String()
        newMember.Conn.CurrentParty = uuid
        p.Lock()
        p.PartialPartys[uuid] = Party{Accepting: true, Members: make(map[string]Member), Ready: make(chan *Connection), Decline: make(chan *Connection)}
        p.PartialPartys[uuid].Members[newMember.Conn.Identifier] = newMember
        p.Unlock()
    }
}

我将->>>>>>放在正在访问2段代码的位置旁边,我不确定如何在不进行数据竞争的情况下保持这些2更新,相当新的想知道如何在没有数据竞争的情况下阅读和编写这个变量。

1 个答案:

答案 0 :(得分:1)

您的问题中有很多代码,但看起来您正在尝试从一个goroutine中的地图(party.Members)中删除元素,而在另一个goroutine中将其循环。这听起来像是一场难以维持,错误缠身的灾难,但是没有记忆比赛就可以做到。

您需要一个互斥锁来保护对地图的访问(包括读取和写入),而困难的部分是确保在for/range迭代期间保持锁定。这是一种方法,通过在for循环开始之前保持锁定,并在循环体内解锁它。

var mut sync.Mutex
var m = map[string]int{}

func f(key string) {
    mut.Lock()
    defer mut.Unlock()
    delete(m, key)
}

func g() {
    mut.Lock()
    defer mut.Unlock()
    for k, v := range m {
        mut.Unlock()
        fmt.Println(k, v)
        mut.Lock()
    }
}

此处,fg的任意组合可以在没有记忆竞赛的情况下同时调用。

理解上更简单的是不解锁/锁定循环内的互斥锁,这意味着f中的删除会等待g中的任何正在运行的循环完成(反之亦然) )。