2013-09-28 3 views
3

Я пытаюсь понять один из Golang typical data races, где доступ к незащищенной глобальной переменной из нескольких goroutines может вызвать состояние гонки:гонки данных при GOMAXPROCS = 1

var service map[string]net.Addr 

func RegisterService(name string, addr net.Addr) { 
    service[name] = addr 
} 

func LookupService(name string) net.Addr { 
    return service[name] 
} 

Он продолжает говорить, что мы может решить это, защитив его мьютексом:

var (
    service map[string]net.Addr 
    serviceMu sync.Mutex 
) 

func RegisterService(name string, addr net.Addr) { 
    serviceMu.Lock() 
    defer serviceMu.Unlock() 
    service[name] = addr 
} 

func LookupService(name string) net.Addr { 
    serviceMu.Lock() 
    defer serviceMu.Unlock() 
    return service[name] 
} 

Пока что так хорошо. Что меня смущает это:

Принятая ответ this question предполагает, что центральный процессор переплете goroutine будет голодать любой другой goroutines, которые были мультиплексированы на одной и той же ОС нить (если мы явно не выход с runtime.Gosched()). Это имеет смысл.

Для меня функции RegisterService() и LookupService() выше выглядят как ЦП, так как нет ввода-вывода и нет выхода. Это верно?

Если это так, и если GOMAXPROCS установлен в 1, то является ли это мьютексом в приведенном выше примере, по-прежнему строго необходимым? Не заботится ли о том, что goroutines привязаны к процессору в точке, где могут возникать условия гонки?

Даже если это так, я полагаю, что в реальной жизни, используя мьютекс, здесь по-прежнему хорошая идея, поскольку мы не можем гарантировать, что GOMAXPROCS установлен. Есть ли еще другие причины?

+0

Тем фактом, что подпрограммы Go являются ЦП не означает, что есть нет никаких шансов на то, что процесс будет происходить между ними. Мьютекс по-прежнему необходим. – fuz

+0

Спасибо. Таким образом, goroutine, связанный с процессором, может голодать другими гортанами в одном и том же потоке ОС, иначе это может не произойти. Мы не можем гарантировать это в любом случае. Если да, то знаете ли вы, какие факторы определяют, голова ли голова других или нет? –

+6

CPU Bound goroutine используется для голодания других в одном и том же потоке, но Go 1.2 (почти выпущенный) имеет упреждающий планировщик, так что это уже не так. Это была только воплощение детской ИМХО. (В go 1.2 каждый вызов функции является возможностью для упреждения, поэтому, если ваша функция привязки процессора не вызывает другие функции, она будет голодать на других в одном потоке). –

ответ

0

Как отметил FUZxxl и Nick Craig-Wood, текущее поведение goroutines является специфичным для реализации. Так что, может быть, чтение или запись на карту может уступить. Учитывая, что для правильного одновременного доступа требуется maps are not thread safe, мьютексы или другая синхронизация.

В качестве альтернативы мьютекса, вы можете породить goroutine, который выполняет все операции по карте и поговорить с ним с каналами:

type writereq struct { 
    key string 
    value net.Addr 
    reply chan struct{} 
} 

type readreq struct { 
    key string 
    reply chan net.Addr 
} 

var service map[string]net.Addr 
var reads = make(chan readreq) 
var writes = make(chan writereq) 

func RegisterService(name string, addr net.Addr) { 
    w := writereq{name, addr, make(chan struct{})} 
    writes <- w 
    return <-w.reply // return after registration confirmation 
} 

func LookupService(name string) net.Addr { 
    r := readreq{name, make(chan net.Addr)} 
    reads <- r 
    return <-r.reply 
} 

func serveRegistry() { 
    for { 
     select { 
     case r := <-reads: 
      r.reply <- service[r.name] 
     case w := <-writes: 
      service[w.name] = w.addr 
      w.reply <- struct{} 
     } 
    } 
} 
Смежные вопросы