减少并发 Set 带来数据竞争

我正在针对我的 redigo 函数运行此测试,看看它是否支持大量并发写入,这是代码


import (

    "github.com/gomodule/redigo/redis"

    "log"

    "os"

)


// Redis connection pool

var RedisPool *redis.Pool


func InitPool() {

    RedisPool = &redis.Pool{

        MaxIdle:   80,

        MaxActive: 12000,

        Dial: func() (redis.Conn, error) {

            conn, err := redis.Dial("tcp", "127.0.0.1:6379")

            if err != nil {

                log.Printf("ERROR: fail init redis: %s", err.Error())

                os.Exit(1)

            }

            return conn, err

        },

    }

}


func ClosePool() error {

    return RedisPool.Close()

}


func Set(key string, val string) error {

    // get conn and put back when exit from method

    conn := RedisPool.Get()

    defer conn.Close()


    _, err := conn.Do("SET", key, val)

    if err != nil {

        log.Printf("ERROR: fail set key %s, val %s, error %s", key, val, err.Error())

        return err

    }


    return nil

}


func TestManySets(t *testing.T) {

    InitPool()

    defer ClosePool()


    var wg sync.WaitGroup

    numOfOperations := 1000

    for i := 0; i < numOfOperations; i++ {

        wg.Add(1)

        go func() {

            err := Set("key", strconv.Itoa(i))

            if err != nil {

                t.Errorf("error when setting key value: %s", err)

            }

            wg.Done()

        }()

    }

    wg.Wait()


    result, err := Get("key")

    if err != nil {

        t.Errorf("error when getting key value: %s", err)

    }

    t.Logf("result: %s", result)

}


当运行测试时,go test -run TestManySets /path/to/package -count 1 -v我遇到了很多EOF错误;


当使用 运行测试时-race,即go test -race -run TestManySets /path/to/package -count 1 -v我检测到大量数据竞争,任何人都可以向我指出如何才能使事情正确?


拉莫斯之舞
浏览 68回答 1
1回答

一只名叫tom的猫

竞争是因为你的 for 循环正在更新,i而你的 goroutine 正在i同时读取。解决这个问题的一种方法是传递i给你的 goroutine 函数:for i := 0; i < numOfOperations; i++ {    wg.Add(1)    go func(i int) {                             // <----------- CHANGE THIS        err := Set("key", strconv.Itoa(i))        if err != nil {            t.Errorf("error when setting key value: %s", err)        }        wg.Done()    }(i)                                         // <----------- AND THIS}这样你就不再有一个闭包i,并且i你的 goroutine 函数内部是一个独特的值,可以在不受外部干扰的情况下读取(或写入)。这还解决了竞争检测器找不到的另一个错误:在循环中for,重复使用递增的变量,这意味着您当前的版本实际上i在许多情况下无意中使用相同的值,并跳过其他值。
打开App,查看更多内容
随时随地看视频慕课网APP