2 回答

TA貢獻(xiàn)1833條經(jīng)驗 獲得超4個贊
您不需要額外的互斥鎖,您的主要功能是在counter.counter不使用原子負(fù)載的情況下讀取,而您的incrementor調(diào)用wg.Done()before counter.Cancel(),因此您會獲得競爭條件。
通過在競爭條件解決wg.Done()后移動:counter.Cancel()
func main() {
wg := sync.WaitGroup{}
numberOfLoops := 10
wg.Add(numberOfLoops)
for i := 0; i < numberOfLoops; i++ {
go incrementor(&wg)
}
wg.Wait()
fmt.Println("Final Counter:", counter.counter)
}
func incrementor(wg *sync.WaitGroup) {
rand.Seed(time.Now().UnixNano())
for i := 0; i < 20; i++ {
counter.Inc()
time.Sleep(time.Duration(rand.Intn(3)) * time.Millisecond)
}
counter.Cancel()
wg.Done()
}

TA貢獻(xiàn)1780條經(jīng)驗 獲得超4個贊
為了避免“可被比賽檢測器檢測到”的比賽:
正如@caveman 回答的那樣,您遇到的問題與wg.Done() / wg.Wait()指令排序中的問題有關(guān),并且您不使用 anatomic.Load()來訪問counter.counter.
對于這種競爭條件,您的方法是“安全的”。
為了避免比賽,如“不要為計數(shù)器達(dá)到不連貫的狀態(tài)”:
你確實有一個問題,(*)因為你的方法運行了幾個連續(xù)的指令來檢查和更新對象(例如 : if condition { update_fields }),并且你沒有同步機(jī)制來檢查condition應(yīng)用時是否仍然正確update_fields。
通過將您的incrementor功能更改為:
func incrementor(wg *sync.WaitGroup) {
for i := 0; i < 20000; i++ {
counter.Inc()
}
counter.Cancel()
wg.Done()
}
并多次運行您的程序,您應(yīng)該能夠看到“Final Counter:”并不總是以 0(操場)結(jié)尾。
這是如何發(fā)生這種情況的說明:
假設(shè) goroutine 1 執(zhí)行counter.Cancel()
而 goroutine 2 執(zhí)行counter.Inc()
可能會發(fā)生以下執(zhí)行順序:
goroutine 1 goroutine 2
1. if c.isFinished() {
return fmt.Errorf("counter is finished")
}
2. if !c.isFinished() {
3. atomic.StoreUint32(&c.finished, 1)
4. atomic.StoreUint64(&c.counter, 0)
}
5. atomic.AddUint64(&c.counter, 1)
6. return nil
中的c.isFinished()指令.Inc()可能在執(zhí)行之前發(fā)生, .Cancel()
并且atomic.AddUint64(&c.counter, 1)可能在將計數(shù)器重置為零之后發(fā)生。 .Cancel()
為了避免這種競爭,您需要選擇一種同步inspect + update指令的方式。
一種常見的方法是使用互斥鎖:
type Counter struct {
counter uint64
finished uint32
m sync.Mutex
}
// Inc increments the counter by one
func (c *Counter) Inc() error {
c.m.Lock()
defer c.m.Unlock()
if c.finished != 0 {
return fmt.Errorf("counter is finished")
}
c.counter++
return nil
}
// Dec decrements the counter by one, but prevents the counter from going to zero
func (c *Counter) Dec() {
c.m.Lock()
defer c.m.Unlock()
// prevent overflow
if c.counter > 0 {
c.counter--
}
}
// Cancel sets the finished flag, and sets counter to zero
func (c *Counter) Cancel() {
c.m.Lock()
defer c.m.Unlock()
if c.finished == 0 {
c.finished = 1
c.counter = 0
}
}
- 2 回答
- 0 關(guān)注
- 117 瀏覽
添加回答
舉報