抢红包
红包定义
id 红包唯一标识 []uint 红包金额
业务分析
发红包: 预先创建 有id 金额 数量 的 红包对象 抢红包: 一个个goroutine带着红包id 去 访问 任务通道,另起一个goroutine 监听 任务通道 并 修改 红包对象 的 金额和数量
定义全局变量
const TaskNums = 5 // 并发任务数 var( // 记录红包已抢总金额 sum uint = 0 TasksChan = make([]chan task, TaskNums) // 任务通道 random_seed = rand.New(rand.NewSource(time.Now().UnixNano())) // 分配的随机数 // 使用并发安全字典表示红包集 // key: uint32 为红包id // value: []uint 为红包内随机金额的列表 packageList = new(sync.Map) mu sync.Mutex )
结构体定义
type task struct{ id uint32 // 表示红包id callback chan uint // 表示返回的金额 }
创建工程目录&文件
mkdir redpack && cd redpack && go mod init redpack touch main.go utils.go
// main.go package main import( // "fmt" "sync" ) func main(){ id, money, num := 88, 100, 10 // 发红包 SetRedPack(id, money, num) // 构造抢红包任务通道 for i:=0;i<TaskNums;i++{ TasksChan[i] = make(chan task) } // 开启goroutine监听任务通道 go GetPackageMoney(TasksChan) var wg sync.WaitGroup // 抢红包 for i:=0;i<num+5;i++{ wg.Add(1) go GetRedPack(id, &wg) } wg.Wait() }
// utils.go package main import( "fmt" "sync" "time" "math/rand" ) // 抢红包任务结构体 type task struct{ id uint32 // 表示红包id callback chan uint // 表示返回的金额 } const TaskNums = 5 // 并发任务数 var( // 记录红包已抢总金额 sum uint = 0 TasksChan = make([]chan task, TaskNums) // 任务通道 random_seed = rand.New(rand.NewSource(time.Now().UnixNano())) // 分配的随机数 // 使用并发安全字典表示红包集 // key: uint32 为红包id // value: []uint 为红包内随机金额的列表 packageList = new(sync.Map) mu sync.Mutex ) // 发红包方法 func SetRedPack(id, money, num int){ left_money, left_num := money,num money_list := make([]uint, num) for left_money > 0{ if left_num == 1{ money_list[num - 1] = uint(left_money) break } if left_num == left_money{ for i:=0;i<left_num;i++{ money_list[i] = 1 } break } rMoney := int(2 * float64(left_money) / float64(left_num)) // 保证分配金额>=1 rand_m := random_seed.Intn(rMoney) + 1 money_list[num - left_num] = uint(rand_m) left_money -= rand_m left_num-- } packageList.Store(uint32(id), money_list) } // 监听任务通道方法 func GetPackageMoney(taskschan []chan task){ for { select{ case t := <- taskschan[0]: GetMoney(t) case t := <- taskschan[1]: GetMoney(t) case t := <- taskschan[2]: GetMoney(t) case t := <- taskschan[3]: GetMoney(t) case t := <- taskschan[4]: GetMoney(t) default: continue } } } func GetMoney(t task){ id := t.id l_money_list, ok := packageList.Load(id) if ok && l_money_list != nil{ list := l_money_list.([]uint) r_index := random_seed.Intn(len(list)) money := list[r_index] // 更新红包金额列表信息 if len(list) > 1{ if r_index == 0{ packageList.Store(id, list[1:]) }else if r_index == len(list) - 1{ packageList.Store(id, list[:1]) }else{ packageList.Store(id, append(list[:r_index], list[r_index+1:]...)) } }else{ packageList.Delete(id) } t.callback <- money }else{ t.callback <- 0 } } func GetRedPack(id int, wg *sync.WaitGroup){ defer wg.Done() // 并发安全字典取 value list1, ok := packageList.Load(uint32(id)) list := list1.([]uint) // 没取到就代表红包不存在 if !ok || len(list) < 1 { fmt.Printf("红包不存在,id=%d\n", id) } // 构造抢红包任务 callback := make(chan uint) t := task{ id: uint32(id), callback: callback, } TasksChan[sum%TaskNums] <- t money := <- t.callback if money <= 0 { fmt.Printf("很遗憾,你没有抢到红包\n") } else { fmt.Printf("恭喜你抢到一个红包,金额为:%d\n", money) mu.Lock() defer mu.Unlock() sum+=money } }
测试
$ go run main.go utils.go 恭喜你抢到一个红包,金额为:7 恭喜你抢到一个红包,金额为:3 恭喜你抢到一个红包,金额为:5 恭喜你抢到一个红包,金额为:15 恭喜你抢到一个红包,金额为:16 恭喜你抢到一个红包,金额为:3 恭喜你抢到一个红包,金额为:15 恭喜你抢到一个红包,金额为:17 恭喜你抢到一个红包,金额为:16 很遗憾,你没有抢到红包 很遗憾,你没有抢到红包 很遗憾,你没有抢到红包 很遗憾,你没有抢到红包 很遗憾,你没有抢到红包 很遗憾,你没有抢到红包
幸运大转盘
...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。