2013-09-28 88 views
3

我想了解Golang typical data races之一,訪問來自多個夠程不受保護的全局變量可能導致競爭條件:數據競爭時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表明,一個CPU綁定的goroutine將任何餓死已複用到相同的操作系統線程(除非我們明確地runtime.Gosched()收益率)等夠程。這是有道理的。

對我來說,上面的RegisterService()LookupService()函數看起來是CPU綁定的,因爲沒有IO和沒有良率。它是否正確?

如果是,並且GOMAXPROCS設置爲1,那麼在上面的示例中,互斥體仍然是嚴格必要的嗎?在競爭條件可能發生的地點,goroutines是否受到CPU限制的事實不是照顧它的事實嗎?

即使這樣做,我認爲在現實生活中使用互斥鎖在這裏仍然是一個好主意,因爲我們可能無法保證GOMAXPROCS被設置爲什麼。還有其他原因嗎?

+0

的圍棋程序是CPU密集型並不意味着存在的事實沒有機會在兩者之間進行調度。互斥體仍然是必需的。 – fuz

+0

謝謝。因此,一個CPU綁定的goroutine可能會在同一個OS線程上餓死其他goroutine,或者它可能不會。無論如何我們無法保證。如果是這樣,你是否知道什麼因素決定了一家公寓是否餓死別人? –

+6

CPU綁定的goroutine用於在同一線程上捱餓,但Go 1.2(幾乎已發佈)擁有先發制人的調度器,因此不再是這種情況。這只是一個實施細節恕我直言。 (在go 1.2中,每個函數調用都是搶佔的機會,所以如果你的CPU綁定函數沒有調用其他函數,它會在同一個線程上餓死其他函數)。 –

回答

0

由於FUZxxl和Nick Craig-Wood指出,goroutines的當前行爲是特定於實現的。所以,也許,讀或寫地圖可以屈服。考慮到maps are not thread safe,正確的併發訪問需要互斥或其他同步。

作爲替代互斥體,可以產生一個夠程,執行你的地圖上的所有操作,並與它對話與渠道:

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{} 
     } 
    } 
}