package main
import (
"fmt"
"math/rand"
"runtime"
"sync"
"time"
)
var bufPool = &sync.Pool{
New: func() any {
buffer := make([]byte, 0, 2)
return &buffer
},
}
func main() {
// 1. 将 GOMAXPROCS 设置为较小值(如 2),强制 goroutine 在少量 P 上高频切换,
// 增加对象进入全局共享队列的概率。
runtime.GOMAXPROCS(2)
var wg sync.WaitGroup
concurrency := 1000 // 高并发数
iterations := 100 // 每个 goroutine 循环次数
for i := 0; i < concurrency; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
for j := 0; j < iterations; j++ {
bufPtr := bufPool.Get().(*[]byte)
buf := *bufPtr
fmt.Printf("cap is %d \n", cap(buf))
// 【故意不重置】模拟忘记 buf = buf[:0] 的致命错误
// buf = buf[:0]
// 写入特定标识:Goroutine ID + 循环次数
marker := fmt.Sprintf("[G%d-I%d-%s]", id, j,RandRandomLenStr(5,100))
buf = append(buf, []byte(marker)...)
// 检查脏数据:如果切片长度大于当前写入标识的长度,
// 说明前面有上一次残留的数据!
if len(buf) > len(marker) {
fmt.Printf("💥 抓到脏数据! Goroutine %d 拿到了残留内容: %s\n", id, string(buf))
// 发现一次即可,防止控制台被刷爆
wg.Done() // 抵消一次 wg.Add
return
}
// 归还到池中(没有重置长度)
*bufPtr = buf
bufPool.Put(bufPtr)
}
}(i)
}
wg.Wait()
fmt.Println("测试完成,如果没有打印💥,说明运气好没触发,可以尝试增加并发数。")
}
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
func RandRandomLenStr(minLen, maxLen int) string {
if minLen <= 0 || maxLen < minLen {
return ""
}
rand.Seed(time.Now().UnixNano())
// 随机长度
length := rand.Intn(maxLen-minLen+1) + minLen
b := make([]byte, length)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}