Redigo 并发集导致数据竞争
Redigo concurrent Set gives data race
我正在 运行 对我的 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
我检测到大量数据竞争,任何人都可以向我指出我该如何做对吧?
以下是数据竞争日志:
ycx@DESKTOP-NBD349L:/mnt/c/Users/robbi/Projects/GoGameServer$ go test -race -run TestManySets visiontech.com/adapter -count 1 -v
=== RUN TestManySets
==================
WARNING: DATA RACE
Read at 0x00c00009ab10 by goroutine 7:
visiontech.com/adapter.TestManySets.func1()
/home/ycx/Projects/GoGameServer/src/visiontech.com/adapter/redis_test.go:48 +0x3c
Previous write at 0x00c00009ab10 by goroutine 6:
visiontech.com/adapter.TestManySets()
/home/ycx/Projects/GoGameServer/src/visiontech.com/adapter/redis_test.go:45 +0x14c
testing.tRunner()
/usr/local/go/src/testing/testing.go:865 +0x163
Goroutine 7 (running) created at:
visiontech.com/adapter.TestManySets()
/home/ycx/Projects/GoGameServer/src/visiontech.com/adapter/redis_test.go:47 +0x128
testing.tRunner()
/usr/local/go/src/testing/testing.go:865 +0x163
Goroutine 6 (running) created at:
testing.(*T).Run()
/usr/local/go/src/testing/testing.go:916 +0x65a
testing.runTests.func1()
/usr/local/go/src/testing/testing.go:1157 +0xa8
testing.tRunner()
/usr/local/go/src/testing/testing.go:865 +0x163
testing.runTests()
/usr/local/go/src/testing/testing.go:1155 +0x523
testing.(*M).Run()
/usr/local/go/src/testing/testing.go:1072 +0x2eb
main.main()
_testmain.go:62 +0x222
==================
--- FAIL: TestManySets (10.08s)
redis_test.go:62: result: 1000
testing.go:809: race detected during execution of test
FAIL
FAIL visiontech.com/adapter 10.098s
ycx@DESKTOP-NBD349L:/mnt/c/Users/robbi/Projects/GoGameServer$
而 redis_test.go 第 47 行是上面代码中 Set
发生的地方。
竞争是因为您的 for 循环正在更新 i
而您的 goroutines 正在同时读取 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
的闭包,并且 goroutine 函数中的 i
是一个可以读取(或写入)而不受外部干扰的独特值。
这也解决了另一个错误,竞争检测器找不到:在 for
循环中,递增的变量被重新使用,这意味着您当前的版本实际上无意中使用了相同的 i
值在很多情况下,并跳过其他人。有关详细信息,请参阅 here。
我正在 运行 对我的 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
我检测到大量数据竞争,任何人都可以向我指出我该如何做对吧?
以下是数据竞争日志:
ycx@DESKTOP-NBD349L:/mnt/c/Users/robbi/Projects/GoGameServer$ go test -race -run TestManySets visiontech.com/adapter -count 1 -v
=== RUN TestManySets
==================
WARNING: DATA RACE
Read at 0x00c00009ab10 by goroutine 7:
visiontech.com/adapter.TestManySets.func1()
/home/ycx/Projects/GoGameServer/src/visiontech.com/adapter/redis_test.go:48 +0x3c
Previous write at 0x00c00009ab10 by goroutine 6:
visiontech.com/adapter.TestManySets()
/home/ycx/Projects/GoGameServer/src/visiontech.com/adapter/redis_test.go:45 +0x14c
testing.tRunner()
/usr/local/go/src/testing/testing.go:865 +0x163
Goroutine 7 (running) created at:
visiontech.com/adapter.TestManySets()
/home/ycx/Projects/GoGameServer/src/visiontech.com/adapter/redis_test.go:47 +0x128
testing.tRunner()
/usr/local/go/src/testing/testing.go:865 +0x163
Goroutine 6 (running) created at:
testing.(*T).Run()
/usr/local/go/src/testing/testing.go:916 +0x65a
testing.runTests.func1()
/usr/local/go/src/testing/testing.go:1157 +0xa8
testing.tRunner()
/usr/local/go/src/testing/testing.go:865 +0x163
testing.runTests()
/usr/local/go/src/testing/testing.go:1155 +0x523
testing.(*M).Run()
/usr/local/go/src/testing/testing.go:1072 +0x2eb
main.main()
_testmain.go:62 +0x222
==================
--- FAIL: TestManySets (10.08s)
redis_test.go:62: result: 1000
testing.go:809: race detected during execution of test
FAIL
FAIL visiontech.com/adapter 10.098s
ycx@DESKTOP-NBD349L:/mnt/c/Users/robbi/Projects/GoGameServer$
而 redis_test.go 第 47 行是上面代码中 Set
发生的地方。
竞争是因为您的 for 循环正在更新 i
而您的 goroutines 正在同时读取 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
的闭包,并且 goroutine 函数中的 i
是一个可以读取(或写入)而不受外部干扰的独特值。
这也解决了另一个错误,竞争检测器找不到:在 for
循环中,递增的变量被重新使用,这意味着您当前的版本实际上无意中使用了相同的 i
值在很多情况下,并跳过其他人。有关详细信息,请参阅 here。