免费注册 查看新帖 |

Chinaunix

广告
  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2207 | 回复: 6
打印 上一主题 下一主题

[C] 关于生产者和消费者 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2015-10-20 17:34 |只看该作者 |倒序浏览
本帖最后由 Unix_C_Linux 于 2015-10-20 17:36 编辑

     情景:缓冲区放入1000数, 生产者和消费者操作分别存取
         1.当生产者和消费者数量是1:1时,写数据记录和读数据记录相等,都是1000
         2.当生产者和消费者数量是1:N时,写数据的数量是1000,为什么读数据的数量之和大于1000?很多都是重复的,多个线程间是有锁和条件变量的,为什么会读出同样的数据啊?

   代码如下,编译后可以直接运行。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define BUFFER_SIZE 16 // 缓冲区数量
  4. struct prodcons
  5. {
  6.         // 缓冲区相关数据结构
  7.         int buffer[BUFFER_SIZE]; /* 实际数据存放的数组*/
  8.         pthread_mutex_t lock; /* 互斥体lock 用于对缓冲区的互斥操作 */
  9.         int readpos, writepos; /* 读写指针*/
  10.         pthread_cond_t notempty; /* 缓冲区非空的条件变量 */
  11.         pthread_cond_t notfull; /* 缓冲区未满的条件变量 */
  12. };
  13. /* 初始化缓冲区结构 */
  14. void init(struct prodcons *b)
  15. {
  16.         pthread_mutex_init(&b->lock, NULL);
  17.         pthread_cond_init(&b->notempty, NULL);
  18.         pthread_cond_init(&b->notfull, NULL);
  19.         b->readpos = 0;
  20.         b->writepos = 0;
  21. }
  22. /* 将产品放入缓冲区,这里是存入一个整数*/
  23. void put(struct prodcons *b, int data)
  24. {
  25.         pthread_mutex_lock(&b->lock);
  26.         /* 等待缓冲区未满*/
  27.         if ((b->writepos + 1) % BUFFER_SIZE == b->readpos)
  28.         {
  29.                 pthread_cond_wait(&b->notfull, &b->lock);
  30.         }
  31.        
  32.         if(data != -1){
  33.                 printf("putdata\n");
  34.         }
  35.         /* 写数据,并移动指针 */
  36.         b->buffer[b->writepos] = data;
  37.         b->writepos = (b->writepos + 1)%BUFFER_SIZE;
  38.         /* 设置缓冲区非空的条件变量*/
  39.         pthread_cond_signal(&b->notempty);
  40.         pthread_mutex_unlock(&b->lock);
  41. }
  42. /* 从缓冲区中取出整数*/
  43. int get(struct prodcons *b)
  44. {
  45.         int data;
  46.         pthread_mutex_lock(&b->lock);
  47.         /* 等待缓冲区非空*/
  48.         if (b->writepos == b->readpos)
  49.         {
  50.                 pthread_cond_wait(&b->notempty, &b->lock);
  51.         }
  52.         /* 读数据,移动读指针*/
  53.         data = b->buffer[b->readpos];
  54.         if(data != -1){
  55.                 printf("getdata\n", data);
  56.         }
  57.         b->readpos = (b->readpos + 1) % BUFFER_SIZE;
  58.         /* 设置缓冲区未满的条件变量*/
  59.         pthread_cond_signal(&b->notfull);
  60.         pthread_mutex_unlock(&b->lock);
  61.         return data;
  62. }

  63. /* 测试:生产者线程将1 到10000 的整数送入缓冲区,消费者线
  64.    程从缓冲区中获取整数,两者都打印信息*/
  65. #define OVER ( - 1)
  66. struct prodcons buffer;
  67. void *producer(void *data)
  68. {
  69.         int n;
  70.         for (n = 0; n < 1000; n++)
  71.         {
  72.                 //printf("%d --->\n", n);
  73.                 put(&buffer, n);
  74.         }
  75.            put(&buffer, OVER);
  76.            put(&buffer, OVER);
  77.            put(&buffer, OVER);
  78.         return NULL;
  79. }

  80. void *consumer(void *data)
  81. {
  82.         int d;
  83.         while (1)
  84.         {
  85.                 d = get(&buffer);
  86.                 if (d == OVER)
  87.                         break;
  88.         //        printf("xxx|%d\n", d);
  89.         }
  90.         return NULL;
  91. }

  92. int main(void)
  93. {
  94.         pthread_t th_a, th_b, th_c, th_d;
  95.         void *retval;
  96.         init(&buffer);
  97.         /* 创建生产者和消费者线程*/
  98.         pthread_create(&th_a, NULL, producer, 0);
  99.         pthread_create(&th_b, NULL, consumer, 0);
  100.         pthread_create(&th_c, NULL, consumer, 0);
  101.         pthread_create(&th_d, NULL, consumer, 0);
  102.         /* 等待四个线程结束*/
  103.         pthread_join(th_a, &retval);
  104.         pthread_join(th_b, &retval);
  105.         pthread_join(th_c, &retval);
  106.         pthread_join(th_d, &retval);
  107.         return 0;
  108. }
复制代码

论坛徽章:
4
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:56:11IT运维版块每日发帖之星
日期:2016-08-11 06:20:00IT运维版块每日发帖之星
日期:2016-08-15 06:20:00
2 [报告]
发表于 2015-10-20 18:14 |只看该作者
一个小问题,需要#include <pthread.h>
问题出在get之前没有判断队列是否为空。修复后的代码片段如下:
  1. 44 /* 从缓冲区中取出整数*/
  2. 45 int get(struct prodcons *b)
  3. 46 {      
  4. 47         int data;
  5. 48         pthread_mutex_lock(&b->lock);
  6. 49         /* 等待缓冲区非空*/
  7. 50         if (b->writepos == b->readpos)
  8. 51         {      
  9. 52                 pthread_cond_wait(&b->notempty, &b->lock);
  10. 53         }
  11. 54         
  12. 55         if (b->writepos != b->readpos) {
  13. 56             /* 读数据,移动读指针*/
  14. 57             data = b->buffer[b->readpos];
  15. 58             if(data != -1) {
  16. 59                 printf("getdata: %d\n", data);
  17. 60             }
  18. 61             b->readpos = (b->readpos + 1) % BUFFER_SIZE;
  19. 62             /* 设置缓冲区未满的条件变量*/
  20. 63             pthread_cond_signal(&b->notfull);
  21. 64         }
  22. 65         pthread_mutex_unlock(&b->lock);
  23. 66         return data;
  24. 67 }
复制代码

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
3 [报告]
发表于 2015-10-20 20:55 |只看该作者
Unix_C_Linux 发表于 2015-10-20 17:34
情景:缓冲区放入1000数, 生产者和消费者操作分别存取
         1.当生产者和消费者数量是1:1时,写数据 ...

59,60行对调一下。

论坛徽章:
0
4 [报告]
发表于 2015-10-21 09:17 |只看该作者
回复 2# happy_fish100


我知道线程A pthread_cond_wait会解锁互斥量后进入等待状态,解锁后别的线程B可以进入。如果此时生产者放入数据通知了线程A,而B正好进入临界区,这时候数据重复取了,是这个原因吗?

   

论坛徽章:
4
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:56:11IT运维版块每日发帖之星
日期:2016-08-11 06:20:00IT运维版块每日发帖之星
日期:2016-08-15 06:20:00
5 [报告]
发表于 2015-10-21 09:40 |只看该作者
本帖最后由 happy_fish100 于 2015-10-21 09:42 编辑

回复 4# Unix_C_Linux

是的,就是你说的这个原因。
所以消费队列前需要重新判断一下队列是否为空,就可以解决队列已被其他线程消费掉的问题。

论坛徽章:
0
6 [报告]
发表于 2015-10-21 13:13 |只看该作者
回复 5# happy_fish100


    3q

论坛徽章:
9
程序设计版块每日发帖之星
日期:2015-10-18 06:20:00程序设计版块每日发帖之星
日期:2015-11-01 06:20:00程序设计版块每日发帖之星
日期:2015-11-02 06:20:00每日论坛发贴之星
日期:2015-11-02 06:20:00程序设计版块每日发帖之星
日期:2015-11-03 06:20:00程序设计版块每日发帖之星
日期:2015-11-04 06:20:00程序设计版块每日发帖之星
日期:2015-11-06 06:20:00数据库技术版块每周发帖之星
日期:2015-12-02 15:02:47数据库技术版块每日发帖之星
日期:2015-12-08 06:20:00
7 [报告]
发表于 2015-10-23 11:33 |只看该作者
谈高性能高并发  队列的选择:  优先选择"lock-free"无锁队列

在做抗DOS处理的代码时, 存在2个线程之间, 必须要交换数据的问题,  其中一个线程是管理线程, 一个是acceptor线程.
对于此类问题, 传统的方式就是: 生产者和消费者模式, 二者之间, 通过队列QUEUE, 双方都加锁(mutex)处理队列.

我的解决方案是采用  Boost 超高性能 无锁队列来实现,  只用了三条代码就解决了此问题.  
这个无锁队列模块已经开发出来多年,  但是国内C++程序员应用不多.  
无锁队列的优势:
它将保证线程无限次调用这个方法都能够在有限步内完成,而不会因为其他线程被阻塞而导致本线程无法在有限步内完成,即“无锁”。
Boost保证在任何情况下所有并发中有一个操作能够得到执行, 不必考虑"多线进程的优先级反转等策略".

三行代码 就可以非常简洁高效率的解决一切锁(mutex)带来的问题, .

#include <boost/lockfree/spsc_queue.hpp>
boost::lockfree::spsc_queue<unsigned int, boost::lockfree::capacity<65536> >  closed_ip_queue;  
closed_ip_queue.push( int_ip );
closed_ip_queue.pop( int_closed_ip )
还要加上检查返回值的代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP