抢红包

  • 红包定义

    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
    很遗憾,你没有抢到红包
    很遗憾,你没有抢到红包
    很遗憾,你没有抢到红包
    很遗憾,你没有抢到红包
    很遗憾,你没有抢到红包
    很遗憾,你没有抢到红包

幸运大转盘

...


BewaterMyfriends
19 声望1 粉丝

IT从业者 数字游民 创业者


下一篇 »
Go - RPC