理解原子加法和互斥量

时间:2018-11-17 11:24:29

标签: go

我正在下面的处理程序函数中测试递增itemID的并发性,有时递增会跳过一个值(例如:4、6、7 ...跳过了ID 5)。

func proxyHandler() http.Handler {
    var itemID int32
    return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
        proxy := httputil.NewSingleHostReverseProxy(url)
        proxy.ModifyResponse = func(res *http.Response) error {
            item := Item{
                ID: int(atomic.AddInt32(&itemID, 1)),
            }
            items.Add(item)
            return nil
        }
        proxy.ServeHTTP(rw, req)
    })
}

我使用Mutex解决了该问题:

func proxyHandler() http.Handler {
    itemID := 0
    mux := sync.Mutex{}
    return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
        proxy := httputil.NewSingleHostReverseProxy(url)
        proxy.ModifyResponse = func(res *http.Response) error {
            mux.Lock()
            itemID++
            item := Item{
                ID: itemID,
            }
            items.Add(item)
            mux.Unlock()
            return nil
        }
        proxy.ServeHTTP(rw, req)
    })
}

我想了解为什么原子加法不如我所期望的那样起作用,即生成没有间隙的连续值。

1 个答案:

答案 0 :(得分:3)

atomic.AddInt32()非常适合多个goroutine并发使用。这就是为什么它位于atomic包中的原因。您遇到的问题是Items.Add(),您在评论中指出该锁定没有任何锁定保护。

这是一个安全的Items.Add()

的粗略定义
type Items struct {
    items []Item
    lock  sync.Mutex
}

func (i *Items) Add(item Item) {
    i.lock.Lock()
    defer i.lock.Unlock()
    i.items = append(i.items, item)
}

有了以上Items的定义,您现在可以将初始代码与atomic.AddInt32()一起使用。但是,我想指出的是,当其他线程附加到Items上时,请勿阅读。甚至读取也必须同步。