Go - 高并发抢到红包实现

// utils.go
package main

import (
"fmt"
"math/rand"
"sync"
"time"
)

// 抢红包任务结构体
type task struct {
id uint32 // 表示红包id
callback chan uint // 表示返回的金额
}

const TaskNums = 5 // 并发任务数

func main() {
id, money, num := 88, 100, 10

  // 发红包  红包ID 金额  数量  
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()
}

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
}
}
posted @   minch  阅读(161)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示