1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-04 21:02:27 +08:00

Compare commits

...

2 Commits
rc ... v2

Author SHA1 Message Date
dudaodong
c0841d7be5 Merge branch 'v2' of github.com:duke-git/lancet into v2 2026-01-29 10:08:21 +08:00
Yang Li
88cf1600e8 fix(random): avoid concurrent rand.Seed causing panic (#345) 2025-12-16 13:52:25 +08:00
2 changed files with 41 additions and 6 deletions

View File

@@ -10,7 +10,7 @@ import (
"io" "io"
"math" "math"
"math/rand" "math/rand"
"os" "sync"
"time" "time"
"unsafe" "unsafe"
@@ -27,7 +27,14 @@ const (
AllChars = Numeral + LowwerLetters + UpperLetters + SymbolChars 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() { func init() {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
@@ -286,10 +293,9 @@ func nearestPowerOfTwo(cap int) int {
// random generate a random string based on given string range. // random generate a random string based on given string range.
func random(s string, length int) string { func random(s string, length int) string {
// 确保随机数生成器的种子是动态的 // 从 pool 中获取 rand.Rand替代全局 rand
pid := os.Getpid() rn := randPool.Get().(*rand.Rand)
timestamp := time.Now().UnixNano() defer randPool.Put(rn)
rand.Seed(int64(pid) + timestamp)
// 仿照strings.Builder // 仿照strings.Builder
// 创建一个长度为 length 的字节切片 // 创建一个长度为 length 的字节切片

View File

@@ -4,6 +4,7 @@ import (
"reflect" "reflect"
"regexp" "regexp"
"strconv" "strconv"
"sync"
"testing" "testing"
"github.com/duke-git/lancet/v2/internal" "github.com/duke-git/lancet/v2/internal"
@@ -376,3 +377,31 @@ func TestRandNumberOfLength(t *testing.T) {
assert := internal.NewAssert(t, "TestRandNumberOfLength") assert := internal.NewAssert(t, "TestRandNumberOfLength")
assert.Equal(6, len(strconv.Itoa(randi))) 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()
}