1、环形缓冲区(下面生产者消费者的例子使用)

使用一段内存空间作为缓冲区,维护两个指针,一是读指针,指向缓冲空间的第一个可读位置;二是写指针,指向空间的第一个空位置。读取一个数据后,读指针+1,当指针位置超出缓冲区域则指向缓冲区域的头位置(置0);写入一个数据后,写指针+1,当指针位置超出缓冲区域则指向缓冲区域的头位置(置0);由于空间循环利用,故称为环形缓冲区。

空间满和空都是读指针位置等于写指针位置。如何判断空间已满?空?方法1:废弃一个缓冲空间不用,当写指针+1等于读指针的时候(意思是写指针多跑一圈快要赶上读指针),此时表明空间已满。方法2:维护一个变量记录缓冲区使用大小,当大小等于缓冲区大小,则为满;当大小为0时为空。

环形缓冲区多用于多线程缓冲。相对于队列,它的优点是不必新增与释放空间,所有空间重复利用,只需维护两个指针。

2、多线程同步

初始化条件变量:pthread_cond_init (pthread_cond_t *cond, const pthread_condattr_t *attr);

第一个参数为条件变量的地址,第二个参数设置为NULL表明默认参数设置,也可设为已初始化的条件变量指针,复制参数设置。返回0为初始化成功。

条件变量阻塞:pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex);

第一个参数设置为需要阻塞的条件变量;第二个参数为用于阻塞的互斥变量

建议使用方式:

pthread_mutex_lock();while(condition_is_false)

pthread_cond_wait();

pthread_mutex_unlock();

一旦则该条件进入阻塞状态,等待pthread_cond_broadcast (pthread_cond_t *cond)(唤醒全部,慎用) ;与pthread_cond_signal (pthread_cond_t *cond)(唤醒其中一个) ;唤醒条件,解除阻塞。

解除条件阻塞:pthread_cond_signal应在互斥保护下使用,否则可能会在检测condition_is_false后与pthread_cond_wait前调用,解除没阻塞的条件。成功返回0。

解除条件变量与当前状态的关联:pthread_cond_destroy(pthread_cond_t *cond);

生产者与消费者的例子:

#define BUFFER_SIZE 16 //缓冲区数量 structprodcons

{//缓冲区相关数据结构 int buffer[BUFFER_SIZE]; /*实际数据存放的数组*/pthread_mutex_tlock; /*互斥体lock 用于对缓冲区的互斥操作*/ int readpos, writepos; /*读写指针*/pthread_cond_t notempty;/*缓冲区非空的条件变量*/pthread_cond_t notfull;/*缓冲区未满的条件变量*/ int size;/*缓冲区当前大小*/};/*初始化缓冲区结构*/ void init(struct prodcons *b)

{

pthread_mutex_init(&b->lock, NULL);

pthread_cond_init(&b->notempty, NULL);

pthread_cond_init(&b->notfull, NULL);

b->readpos = 0;

b->writepos = 0;

b->size=0;

}/*将产品放入缓冲区,这里是存入一个整数*/ void put(struct prodcons *b, intdata)

{

pthread_mutex_lock(&b->lock);/*等待缓冲区未满*/ while(b->size==BUFFER_SIZE)//if ((b->writepos + 1) % BUFFER_SIZE == b->readpos) {

pthread_cond_wait(&b->notfull, &b->lock);//进入等待后会释放互斥,使其他线程进入互斥改变状态 }/*写数据,并移动指针*/b->buffer[b->writepos] =data;

b->writepos++;if (b->writepos >=BUFFER_SIZE)

b->writepos = 0;

b->size++;/*设置缓冲区非空的条件变量*/pthread_cond_signal(&b->notempty);

pthread_mutex_unlock(&b->lock);

}/*从缓冲区中取出整数*/ int get(struct prodcons *b)

{intdata;

pthread_mutex_lock(&b->lock);/*等待缓冲区非空*/ while(b->size==0)//if (b->writepos == b->readpos)//此时缓冲区为空 {

pthread_cond_wait(&b->notempty, &b->lock);

}/*读数据,移动读指针*/data= b->buffer[b->readpos];

b->readpos++;if (b->readpos >=BUFFER_SIZE)

b->readpos = 0;

b->size--;/*设置缓冲区未满的条件变量*/pthread_cond_signal(&b->notfull);

pthread_mutex_unlock(&b->lock);returndata;

}/*测试:生产者线程将1 到10000 的整数送入缓冲区,消费者线

程从缓冲区中获取整数,两者都打印信息*/ #define OVER ( - 1) structprodcons buffer;void *producer(void *data)

{intn;for (n = 0; n < 10000; n++)

{

printf("%d --->\n", n);

put(&buffer, n);

} put(&buffer, OVER);returnNULL;

}void *consumer(void *data)

{intd;while (1)

{

d= get(&buffer);if (d ==OVER)break;

printf("--->%d \n", d);

}returnNULL;

}int main(void)

{

pthread_t th_a, th_b;void *retval;

init(&buffer);/*创建生产者和消费者线程*/pthread_create(&th_a, NULL, producer, 0);

pthread_create(&th_b, NULL, consumer, 0);/*等待两个线程结束*/pthread_join(th_a,&retval);

pthread_join(th_b,&retval);return 0;

}

解释一下pthread_cond_wait的工作流程:

1、pthread_cond_wait必须与pthread_mutex_t协同使用。首先,pthread_mutex_lock(&b->lock);

2、程序查看了缓冲区中,发现缓冲区状态不适合操作,即调用pthread_cond_wait。

3、调用pthread_cond_wait后,mutex的锁马上解除,以便其他线程进行操作,改变缓冲区。

4、mutex解锁后,线程2得以进入改变缓冲区,改变后调用pthread_cond_signal(&b->notfull);唤醒阻塞的线程1.

5、被唤醒后,pthread_cond_wait()先对mutex上锁,再返回。上锁是为了与接下来对缓冲区操作后的解锁相匹配。

最近做多线程数据传输程序时出现了“线程饿死”的现象,在这里说明一下:

当一个线程释放互斥后,CPU时间片还没到时,他又重新进入互斥,使得另外一个生产者线程完全没有机会得到CPU运行权,导致线程无法执行。解决的办法是在释放互斥锁后添加usleep(1),强迫线程进入阻塞,交出CPU运行权,使其他的线程有机会运行

pthread_cond_wait总是与一个布尔判断式相联在一起,如果判断式为真,则线程继续执行,然而pthread_cond_wait返回时无法保证判断式是真是假,因此需要重新判断。

pthread_cond_wait与while循环结合使用来检查判断式:

之前在判断size大小的时候使用了if,导致出现了死锁。上网查了文献,发现pthread_cond_wait应该使用while循环,因为解锁的时候不能保证if的条件成立。就像有两个生产者线程的条件下,一个生产者判断BUFFER没空位后被阻塞了,消费者取走BUFFER的货物后唤醒了这个生产者,但此时,有可能此生产者的时间片用完了,CPU切换到第二个生产者去,第二个生产者又把BUFFER填满了。如果第一个生产者使用的是if,被唤醒后没有再判断size,就直接到已满的BUFFER上操作造成错误了。

When using condition variables there is always a Boolean predicate involving shared variables associated with

each condtion wait that is ture if the thread should proceed...

Since the return does not imply anything about the value of this predicate, the predicate should be re-evaluated upon such return.

强烈建议pthread_cond_wait与while循环结合使用来检查判断式

It is thus recommened that a condition wait be enclosed in the equivalent of a "while loop" that checks the predicate.

linux 多线程环形缓冲区,[多线程]环形缓冲区以及多线程条件同步相关推荐

  1. linux printf 刷新,linux下printf中\n刷新缓冲区的疑问(已解决)

    #include #include int main(void) {          printf("hello world"); close(STDOUT_FILENO);   ...

  2. linux文件系统dentry_Linux文件系统(四)---三大缓冲区之inode缓冲区 (内存inode映像 )...

    在文件系统中,有三大缓冲为了提升效率:inode缓冲区.dentry缓冲区.块缓冲. (内核:2.4.37) 一.inode缓冲区 为了加快对索引节点的索引,引入inode缓冲区,下面我们看Linux ...

  3. linux下查看系统socket读写缓冲区

    一:linux下查看系统socket读写缓冲区大小配置: http://blog.csdn.net/herecles/article/details/8146017 1. tcp 收发缓冲区默认值 [ ...

  4. linux下printf语句执行时间,linux下printf中\n刷新缓冲区的疑问(已解决)

    #include #include int main(void) {          printf("hello world"); close(STDOUT_FILENO);   ...

  5. linux 目录缓冲,Linux文件系统(六)---三大缓冲区之 目录缓冲区dcache

    在文件系统中,有三大缓冲为了提升效率:inode缓冲区.dentry缓冲区.块缓冲. (内核:2.4.37) 为什么这个缓冲区会存在,不好意思,我说了废话,当然和前面一样的,为了提升效率,例如我们写一 ...

  6. wince支持多线程编程吗_以前面试只问多线程,现在都开始问响应式编程了!我懵了...

    以前面试只问多线程,现在都开始问响应式编程了! 看完这篇文章你就能有个大体的了解了. 基本介绍 目前比较流行的编程方法论有函数式编程(functional programming).响应式编程(rea ...

  7. java 缓冲区溢出_缓冲区溢出详解

    1 缓冲区溢出原理 缓冲区是一块连续的计算机内存区域,可保存相同数据类型的多个实例.缓冲区可以是堆栈(自动变量).堆(动态内存)和静态数据区(全局或静态).在C/C++语言中,通常使用字符数组和mal ...

  8. 【Netty】NIO 缓冲区 ( Buffer ) ( 缓冲区读写类型 | 只读缓冲区 | 映射字节缓冲区 )

    文章目录 I . 缓冲区 ( Buffer ) 存取类型 II . 只读缓冲区 ( ReadOnlyBuffer ) III . 映射字节缓冲区 ( MappedByteBuffer ) I . 缓冲 ...

  9. 【Android 高性能音频】AAudio 音频流 缓冲区 简介 ( AAudio 音频流内部缓冲区 | 缓冲区帧容量 | 缓冲区帧大小 | 音频数据读写缓冲区 )

    文章目录 I . AAudio 音频流内部缓冲区 与 音频数据读写缓冲区 概念 II . AAudio 音频流内部缓冲区 缓冲区帧容量 BufferCapacityInFrames 与 缓冲区帧大小 ...

  10. 线程间定制化调用通信—— 1 高内聚低耦合的前提下,线程操作资源类 2 判断/干活/通知 3 多线程交互中,必须要防止多线程的虚假唤醒,也即(判断只用while,不能用if)

    生产者与消费者模式 一个生产者与一个消费者 题目:现在有两个线程,可以操作初始值为0的一个变量,实现一个线程对该变量加1,另一个线程对该变量减1,这两个线程的操作加一.减一交替,进行10轮,变量的初始 ...

最新文章

  1. 线性代数:05 实对称矩阵与二次型
  2. retrofit2 发送json数据_SQLmap JSON 格式的数据注入
  3. c++ 利用boost 实现文件操作
  4. oracle setpage,Oracle Set命令的应用
  5. Spring Boot Jpa多数据源配置
  6. MVC3+Entity Framework 实现投票系统(一)
  7. typescript断言
  8. 牛客小白月赛12 C 华华给月月出题 (积性函数,线性筛)
  9. cmd 220 ftp 远程主机关闭连接_下载ftp软件,下载ftp软件需要注意3点
  10. idea忽略文件不提交git_你可能会忽略的 Git 提交规范
  11. 2019 蓝桥杯省赛 A 组模拟赛(一)阶乘位数
  12. 我的电脑能装苹果吗?
  13. BigGAN(2019)
  14. snagit 9注册码
  15. 汉字转拼音多音字java_汉字转拼音多音字解决方案 搜索引擎分词细胞词库更新 搜狗词库提取TXT PHP高性能输出UNICODE正则汉字列表...
  16. SpringBoot基础-Environment解析
  17. hbase snappy 安装_hbase自带snappy压缩测试出错
  18. python格式化输出(二)--字符串的格式化输出
  19. IDEA Windows + Mac 快捷键(全)
  20. 给iOS App减肥

热门文章

  1. “佐藤可士和”的超整理术 整理真的可以让人愉悦
  2. 决策树桩(Decision Stump)
  3. Openlayers简介
  4. mysql怎么查看网站后台帐号密码_mysql怎么查看网站后台帐号密码
  5. postgre的replace函数
  6. 11个问题,帮你彻底搞懂工业互联网
  7. 怎么用matlab编写quad8算法,MATLAB程序设计教程(8)---MATLAB数值积分与微分
  8. MPU6500驱动调试笔记(STM32F407+SPI)
  9. 计算机专业西交大和哈工大,高考:哈工大和西安交大,两所比肩清华的工科牛校,你怎么选?...
  10. 推荐一款安卓软件:爱家定位,及时定位知道老人和小孩的位置防走失