diff --git a/random/random.go b/random/random.go index 993e23b..60e5815 100644 --- a/random/random.go +++ b/random/random.go @@ -10,7 +10,7 @@ import ( "io" "math" "math/rand" - "os" + "sync" "time" "unsafe" @@ -27,7 +27,14 @@ const ( AllChars = Numeral + LowwerLetters + UpperLetters + SymbolChars ) -var rn = rand.NewSource(time.Now().UnixNano()) +// var rn = rand.NewSource(time.Now().UnixNano()) + +// 每个 goroutine 独立的 rand.Rand,避免并发问题 +var randPool = sync.Pool{ + New: func() any { + return rand.New(rand.NewSource(time.Now().UnixNano())) + }, +} func init() { rand.Seed(time.Now().UnixNano()) @@ -286,10 +293,9 @@ func nearestPowerOfTwo(cap int) int { // random generate a random string based on given string range. func random(s string, length int) string { - // 确保随机数生成器的种子是动态的 - pid := os.Getpid() - timestamp := time.Now().UnixNano() - rand.Seed(int64(pid) + timestamp) + // 从 pool 中获取 rand.Rand(替代全局 rand) + rn := randPool.Get().(*rand.Rand) + defer randPool.Put(rn) // 仿照strings.Builder // 创建一个长度为 length 的字节切片 diff --git a/random/random_test.go b/random/random_test.go index 602cb15..60d549c 100644 --- a/random/random_test.go +++ b/random/random_test.go @@ -4,6 +4,7 @@ import ( "reflect" "regexp" "strconv" + "sync" "testing" "github.com/duke-git/lancet/v2/internal" @@ -376,3 +377,31 @@ func TestRandNumberOfLength(t *testing.T) { assert := internal.NewAssert(t, "TestRandNumberOfLength") assert.Equal(6, len(strconv.Itoa(randi))) } + +// TestRandStringConcurrent verifies RandString is safe under high concurrency. +// Before the fix, this test may panic or trigger data races. +// After the fix, it should always pass. +func TestRandStringConcurrent(t *testing.T) { + const ( + goroutines = 100 + iterations = 1000 + length = 32 + ) + + var wg sync.WaitGroup + wg.Add(goroutines) + + for g := 0; g < goroutines; g++ { + go func() { + defer wg.Done() + for i := 0; i < iterations; i++ { + s := RandString(length) + if len(s) != length { + t.Fatalf("unexpected string length: got %d, want %d", len(s), length) + } + } + }() + } + + wg.Wait() +}