一、背景

想法源于微信、QQ、蓝信抢红包的热情,内部是怎么实现分配处理的呢?
对于单机的情况,是否可以使用多线程去模拟多个用户同时去抢红包?

二、相关知识

大概查找了一下相关的资料[1][2],我理解红包软件实现的主要难点是在存储、分配这两块;存储解决数据原子性的问题、分配则解决先后抢包期望值一致的问题;
2.1 并发操作
用户在微信中抢红包时分成抢包和拆包两个操作。抢包决定红包是否还有剩余金额,但如果行动不够迅速,在拆包阶段可能红包已经被其他用户抢走的情况。”[1];
解决方法是用的CAS去保证并发抢包下的数据原子性(多客户端多个机器的情况下),在本文的多线程模拟中,其实就可以使用线程锁去保证数据的原子性,防止出现1份红包分别被2个人同时拥有;
2.2 金额分配
红包的金额是拆的时候实时计算,而不是预先分配,实时计算基于内存,不需要额外存储空间,并且实时计算效率也很高。每次拆红包时,系统取0.01到剩余平均值*2之间作为红包的金额。"[1];
把红包做成份数去理解会比较清楚一些,红包的当前状态可以表示为 [份数, 金额],如[10,100] 表示当前为10人份总额100元的红包,当客户端拿到 [10,100] 这个状态后,根据seed去获取0.01份~1.99份的当前红包(非最后一份红包的情况);
在实际的微信实现中,这个seed应该就是跟红包id、用户id相关的数据吧,在多线程模拟下,简单起见,直接就用默认的seed就行了;
2.3 相关接口
互斥锁初始化:pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);
上锁:pthread_mutex_lock(pthread_mutex_t *mutex);
解锁:pthread_mutex_unlock(pthread_mutex_t *mutex);
条件变量初始化:pthread_cond_init(pthread_cond_t *cond,const pthread_cond_t *attr);
线程挂起等待:pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);
唤醒单个:pthread_cond_signal(pthread_cond_t *cond);
全部唤醒:pthread_cond_broadcast(pthread_cond_t *cond);

三、实现

定义部分,instance_t 为程序实例结构体,里面放置线程属性、互斥锁、条件量,
item_t 结构体表示红包条目,成员num为当前红包份数,tot表示当前红包总金额,单位是分;
#define THREAD_NUM 10
#define __RAND(min, max) (rand() % ((max) - (min)) + min)typedef struct instance
{pthread_attr_t attr;pthread_mutex_t mutex;pthread_cond_t cond;
} instance_t;typedef struct item
{int num;int tot;
} item_t;static item_t item = {0};

主线程为生产线程由stdin进行控制发红包,发出红包后通过条件变量广播唤醒所有子线程(10个工作子线程负责抢红包)

int main ()
{int ret = FAILURE;int ix = 0;int num = 0;int tot = 0;instance_t inst = {0};pthread_t tid;ASSERT(SUCCESS, ret = pthread_mutex_init(&inst.mutex, NULL));ASSERT(SUCCESS, ret = pthread_cond_init(&inst.cond, NULL));ASSERT(SUCCESS, ret = pthread_attr_init(&inst.attr));ASSERT(SUCCESS, ret = pthread_attr_setschedpolicy(&inst.attr, SCHED_OTHER));for ( ix = 0; ix < THREAD_NUM; ix++ ) {ASSERT(SUCCESS, ret = pthread_create(&tid, &inst.attr, __worker, &inst));}while ( 1 ) {printf("Input: < number > < money > \n");if ( fscanf(stdin, "%d %d", &num, &tot) == EOF ) {break;}if ( num < 0 || num > 10 ) {printf("Package number out of range: [1, 10]\n");continue;}else if ( tot < 0 || tot > 200 ) {printf("Total money out of range: [1, 200]\n");continue;}pthread_mutex_lock(&inst.mutex);item.num = num;item.tot = tot * 100;printf("Init: [%d, %d.%02d]\n", item.num, item.tot / 100, item.tot % 100); pthread_cond_broadcast(&inst.cond); pthread_mutex_unlock(&inst.mutex);sleep(1);}ASSERT(SUCCESS, ret = pthread_attr_destroy(&inst.attr));ASSERT(SUCCESS, ret = pthread_cond_destroy(&inst.cond));ASSERT(SUCCESS, ret = pthread_mutex_destroy(&inst.mutex));
_E1:return EXIT_SUCCESS;
}

对于红包操作为共享资源,所以得用线程锁进行保护,各个子线程抢完红包后挂起休眠;

static void *__worker(void *args)
{int money = 0;instance_t *pinst = (instance_t *)args;if ( !args ) {return NULL;}printf("Start thread: %u\n", (u32)pthread_self());while ( 1 ) {pthread_mutex_lock(&pinst->mutex);pthread_cond_wait(&pinst->cond, &pinst->mutex); if ( item.num <= 0 ) {printf("Thread #%d get %d.%02d, left [%d, %d.%02d]\n", (u32)pthread_self(),0, 0, 0, 0, 0);pthread_mutex_unlock(&pinst->mutex);continue;}else if ( item.num == 1 ) {money = item.tot;}else {/* roll is 0.01 ~ 1.99 */money = item.tot * __RAND(1, 199) / 100 / item.num;}item.tot -= money;item.num--;printf("Thread #%d get %d.%02d, left [%d, %d.%02d]\n",(u32)pthread_self(),money / 100, money % 100, item.num, item.tot / 100, item.tot % 100);pthread_mutex_unlock(&pinst->mutex);}printf("Stop thread: %u", (u32)pthread_self());return NULL;
}

四、总结

本文通过一生产者多消费者的多线程编程去模拟微信抢红包的过程;
从执行结果上看,10个线程同时抢红包,每次发5个红包抢到的结果均是比较贴近手气红包的规律;
而且有个非常有趣的现象,每次居然都是前5个线程手快抢到了红包!修改了线程优先级也是一样的结果,所以猜测与广播唤醒的顺序机制有关;
参考文章:
[1] 微信红包金额分配的算法,http://www.open-open.com/lib/view/open1430473257443.html
[2] 微信红包的架构设计简介,https://www.zybuluo.com/yulin718/note/93148
[3] linux下条件变量、线程锁的使用,http://www.cppblog.com/converse/archive/2009/01/15/72064.aspx

c语言多线程-模拟微信抢红包相关推荐

  1. 用python语言模拟微信红包_python 模拟微信抢红包 基础语法实现demo

    1.实现微信抢红包 简易demo版 : 代码如下: from decimalimport Decimal # 提供了随机方法 import random print('$$$$$$weichat模拟微 ...

  2. java模拟微信抢红包金额算法规则二倍均值法模拟(满满的注释)

    二倍均值法模拟微信抢红包金额算法规则 ```java /*** 二倍均值法* @param amount 总金额* @param min 最小金额* @param num 个数* 本帖只提供思路,实际 ...

  3. 模拟微信抢红包demo,生成随机数

    文章目录 概述 随机数方法方法 Math.random()方法 Random类 抢红包!! 概述 经常抢红包会发现,很大的概率是在一开始得时候抢的红包越大,越靠后越小(大概率是这种情况,这是我的经验之 ...

  4. Java模拟微信抢红包

    import java.util.Scanner; import java.util.Random;public class RedBags{public static void main(Strin ...

  5. matlab程序模拟微信抢红包,js仿微信抢红包功能

    本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下 仿微信抢红包 html,body,div{margin:0;padding:0;} body{background ...

  6. Java应用_模拟微信抢红包

    如何用Java实现一个微信抢红包的过程呢? Main.java package redEnvelope;import java.util.Scanner;public class Main {publ ...

  7. matlab程序模拟微信抢红包,js模拟微信抢红包算法的讨论

    春节在家无聊,抢红包的时候想起来,不如自己写一个微信抢红包算法来练练手.本以为是非常简单的一个事情,但真正写下来也算是一波三折,不禁感叹,在程序员的路上,我还是太嫩了啊!写这篇文章的原因也是想与广大网 ...

  8. c++代码模拟微信抢红包算法,没人抢得过你!

    算法说明: 微信抢红包基本功能为:提示输入红包总金额和红包份数,保证每个红包的金额随机且不为0,每个红包的金额差距不能太大. 下面是一份C/C++学习资料,加小编C/C++学习群:825414254, ...

  9. 微信红包程序c语言,C语言实战番外篇——模拟微信抢红包

    2019.12.3 如图为运行效果 抢红包.png 输入的名字的时候即可中文也可英文 1.输入中文:打完拼音后按下空格键即可 2.输入英文需两次回车,也可先shift,再输入,再回车 该程序用到的知识 ...

最新文章

  1. oauth2中用户的信息如何动态获取和存储_oAuth2.0 简介
  2. 虚机如果要访问SAN中的多个LUN,如何实现高可用
  3. 用文本指导文本:基于文本的自监督可控文本生成模型
  4. Matlab中左除和右除
  5. Spring远程支持和开发RMI服务
  6. 拒绝职场危机,程序员最核心的竞争力是什么?
  7. Madagascar的自定义浮点型函数--绝对值函数和最值函数
  8. 《C语言及程序设计》实践参考——体重监测器
  9. python mongodb查询速度优化_Mongodb 3 查询优化(语句优化、建索引)
  10. matlab中的turbo码,基于Matlab的Turbo码仿真研究
  11. 利用ArcGIS Python批量拼接遥感影像(arcpy batch processing)
  12. mysql将日期转换年份_mysql将日期转换为当前年份的相同日期
  13. php微信公众号回复换行,PHP 微信公众号开发,关键字回复使用switch出错
  14. 用XDOC制作Flash出国签证
  15. HTML translate 属性
  16. 微信小程序自定义组件——手写radio
  17. 【BLE】BQB认证
  18. ITeye4月读书活动之《游戏引擎架构》
  19. C++ 2.吃雪糕吗
  20. NEO4J分析《权力的游戏》人物领土等关系

热门文章

  1. 基于51单片机N76E003无线遥控器系统设计(毕设课设)
  2. 我的创业日记3(我的office)——我的第二个家
  3. Flutter--可拖拽组件封装,支持拖动到列表边缘时图自动滚动!!!基于EdgeDraggingAutoScroller
  4. python制作编程软件的方法_python代码能做成软件吗
  5. 写字楼租金,2020年之后会呈现十分惊人的跳升
  6. 计算机网络(山东联盟)滨州学院,2021知到网课 概率论与数理统计(山东联盟—滨州学院) 单元测试答案...
  7. js权威教程学习笔记
  8. 英伟达 VLSI PD 面经
  9. 靳小强计算机类专场讲座直播视频,靳小强:专业的意义
  10. APS.NET_MVC5学习笔记-给数据模型添加校验器