乒乓缓冲机制

一、认识Ping-pong工作模式
Ping-pong实质是一种数据缓冲的手段(是一种数据传输技术),能够同时利用两个数据缓冲区达到数据连续传输的目的,从而提高数据传输速率。由于单个缓冲区得到的数据在传输和处理中很容易被覆盖,而Ping-pong 缓冲区的方式能够总是保持一个缓冲区的数据被利用,另一个缓冲去用于存储数据。即两个相同的对象作为缓冲区交替地被读和被写。

二、Ping-pong模式&普通数据交换模式

1. 普通数据交换机制
项目中两个模块间交换数据时,第一个模块(上级)向另一个模块发送数据,接受模块(下级)不能马上处理完成并返回,这样上级必须等待下级处理完成才可以送新的数据,这样就会对性能产生很大的损失,影响了数据传输效率。

2. ping-pong数据交换模式
pingpong机制是一种数据交换机制,我们可以不去等待接受模块(下级)处理结束,而是发送模块(上级)继续执行并将结果保存在ping路的缓存中,上级继续执行到一定时刻,下级模块处理完成将结果保存在pong路中),这样可以下级模块无需等待继续执行,上级也无需等待继续执行,转而将结果存储在ping路。这样便提高了处理效率。

3.ping-pong详解

乒乓缓冲机制在很多场合都有应用价值,将其抽象成某种通用化类库,使代码得以复用。那么首先就要抽象出此机制的抽象模型。
  
  乒乓缓冲应该有两个相同的对象作为缓冲区(对象类型可以是任意的),两者交替地被读和被写。在卷轴的例子中,向可见区域移动就是读操作,生成并绘制就是写操作。读写的过程在两个缓冲区之间交替进行:一开始两个缓冲内容均无效,不能被读;然后写0,完毕后0可读,再写1使1可读,同时可以读0,读完后0变成可写状态;1写毕后变成可读状态……由此可见对于每个缓冲区来说,可能的状态有四种并按如下顺序循环往复地转换:可读=>在读=>可写=>在写=>可读=>……
  一个BiBuf类负责维护两个缓冲对象(长度为2的Object类型数组,名为bufs)及记录其状态。状态常量这样设定:0=在读, 1=可读(写毕), 2=可写(读毕), 3=在写。 于是两个缓冲状态的表示用一个长度为2的byte型数组即可(当然完全可以放在一个byte变量的高4位和低4位,不过不够清晰易懂)。但仅有这个指示某块缓冲“可以做什么”的变量还不够,还要有个指示“应该做什么”的变量。因为对于单一的读或写操作来说,可操作的缓冲对象必须是交替轮换的,比如两个缓冲都写满之后,都是可读的状态,这时实际应读哪一个呢?所以还应该有两个变量bufToRead和bufToWrite,分别指示当前应该读和应该写的缓冲序号。
  缓冲对象数组被设成私有的以阻止外部对象直接对其操作。为了读写,BiBuf类将有一个openBuf(char request)方法用于按request指定的读写请求('w'为写,'r'为读)打开一个缓冲对象。方法会阻塞直至“应该”被操作的对象已经“可以”进行指定的操作,然后重新设定该块缓冲对象的状态变量。

4.ping-pong 机制代码实现
BiBuffer.java :

public class BiBuffer {
  //……

public synchronized Object openBuf(char request) {
        //必须用synchronized关键字,以实现对状态变量rwstat读写的互斥
        if (request=='w') {
          //若请求的是写操作
            try {
            while (rwstat[bufToWrite]!=READDONE) {
              //阻塞直至bufToWrite号缓冲可写
                wait();
            }
            }catch(InterruptedException inte){}
            rwstat[bufToWrite]=WRITING;//把状态置为在写
            return bufs[bufToWrite];//返回该块缓冲对象
        }
        else if (request=='r') {
            try {
            while (rwstat[bufToRead]>=READDONE) {
              //阻塞直至bufToRead号缓冲可读
                wait();
            }
            }catch(InterruptedException inte){}
            rwstat[bufToRead]=READING;//把状态置为在读
            readingReaders++;//读者数加1(因为可以被不止一个对象读)
            return copydata(bufs[bufToRead]);//返回该块缓冲对象的拷贝
        }
        else return null;
        
    }
    
    //……
}
  需要解释一下copydata()方法,它的作用是返回一个缓冲对象的拷贝,因为“读”操作与“写”操作的本质区别是,它原则上是不能修改原对象的,所以要返回一个拷贝供读者对象去“读”。但其默认的做法是直接返回原缓冲对象本身,这是出于效率的考虑:
  public Object copydata(Object src) {
     //子类可重写之
     return src;
    }
当然,方法是留给子类去继承的,具体实现依赖于子类的设计。
  为什么要单独设计一个copydata方法而不是把缓冲对象声明为Clonable,直接调用其clone()方法呢?这仅仅是为了不必因为缓冲对象要实现Clonable接口,还要去写一个其原型的子类罢了(何况还应考虑到有的类可能被设置为final,不可继承的)。
  
  一旦一个缓冲对象被openBuf()打开后,其状态值就被标成相应的“正在进行”状态,这时其它的线程要去打开它的话也就只能阻塞直至其完成操作(除了正在读时另外的读者去打开它时,因为“读”是可以多个线程同时进行的),于是就可以放心大胆地对其进行读写。而读写操作完成后,应调用closeBuf(Object buf)方法设置buf所引用的缓冲的状态为“完成”。
  //……
  
    public synchronized boolean closeBuf(Object buf) {
      int bufid;
      if (bufs[0]==buf) bufid=0;
      else if (bufs[1]==buf) bufid=1;
      else return false;
      
        try {
        if (rwstat[bufid]==WRITING) {
            //若该块缓冲是被写的
            rwstat[bufid]=WRITTEN;//设状态为可读
            bufToWrite=1-bufToWrite;//当前应写的缓冲切换到另一个
            return true;
        }
        else if (rwstat[bufid]==READING) {
            //若该块缓冲是被读的
            if ((--readingReaders)==0) rwstat[bufid]=READDONE;//递减读者数量。若减到0则置状态为可写
            bufToRead=1-bufToRead;//当前应读的缓冲切换到另一个
            return true;
        }
        else return false;
        }finally {
            notifyAll();//唤醒所有因条件不满足而等待的线程
        }
        
    }
    
    //……
    
  两个缓冲对象的初始化操作通过方法initdata来完成。它既可以接默认设计直接在由构造方法参数传入的对象数组上进行初始化,也可以自己生成一个新的数组,具体如何做完全交给子类去重写,赋予这个设计以充分的灵活性。下面是该类的其余部分代码:
public class BiBuffer {
    private Object bufs[];
    private byte rwstat[];
    private int bufToRead, bufToWrite;
    private int readingReaders;
    
    public static final byte READING = 0;
    public static final byte WRITTEN = 1;
    public static final byte READDONE = 2;
    public static final byte WRITING = 3;
    
    public Object[] initdata(Object[] data) {
     //子类可重写之
     return data;
    }
    
    public Object copydata(Object src) {
     //子类可重写之
     return src;
    }
    
    public void reset() {
       rwstat[0]=READDONE; rwstat[1]=READDONE;
       readingReaders=0;
       bufToWrite=0; bufToRead=0; 
    }
    
    public BiBuffer(Object[] data) {
       rwstat=new byte[2];
       reset();
       bufs=initdata(data);
    }
    
  //……
  
}
    
  到此为止这个类应该算比较完善了。但还有一点不太令人满意:openBuf(),读写,closeBuf(),这样的操作步骤是要靠读写者自己去做的,有点麻烦,况且不能保证编写程序的人会不会忘记在用完后closeBuf。我希望在提供openBuf()和closeBuf()这两个接口之外,还有一种更方便的办法。下文就介绍这个增强型的设计。

转自: http://www.cnblogs.com/qinjunni/archive/2012/02/20/2359583.html

ping-pong机制相关推荐

  1. POJ 3928 amp; HDU 2492 Ping pong(树阵评价倒数)

    主题链接: PKU:http://poj.org/problem?id=3928 HDU:http://acm.hdu.edu.cn/showproblem.php?pid=2492 Descript ...

  2. Ping pong【树状数组】

    Ping pong UVALive - 4329 题目传送门 题目大意:一条大街上住着n个乒乓球爱好者,经常组织比赛切磋技术.每个人都有一个不同的技能值ai.每场比赛需要三个人:两名选手,一名裁判.他 ...

  3. ping/pong模式_PING的完整形式是什么?

    ping/pong模式 PING:数据包InterNet Groper (PING: Packet InterNet Groper) In the sector of networking of co ...

  4. Unity3D 4.x怎样实现动画的Ping Pong效果

    近期在看Unity官方的Stealth项目教学视频.视频使用的是Unity旧的版本号,而我如今正在使用的是Unity 4.5,动画系统的操作全然不同了.依照视频的方式根本无法设置动画的Ping Pin ...

  5. [UVALive - 4329] Ping pong 树状数组入门

    题目链接:Ping pong 题意 给你n个数,你从中取3个数,要求中间的数字大小在两边数字之间.问你总共有多少种取法. 题解 这个题首先需要分析转化. 假设第i个人作为中间数 a1-ai−1有ci个 ...

  6. HDOJ 2492 Ping pong 线段树+离散化

    //2492 Ping pong 线段树+离散化 /* 题意: 有一陀人从左到右排成一排,每个人有一个唯一的技能值,每个人都找其他人比赛, 比赛前要再找一个人做裁判,裁判的技能值不能比这两个人都高,也 ...

  7. go-gorilla的ping pong

    业务需求,ping每隔60秒执行一次,ping两次后,没有得到pong的消息,自动切断client. pongTime=180 * time.Second pingTime=60 * time.Sec ...

  8. ping pong buffer

    Ping pong buffer is a buffer used in a transmission that contains two separate buffers, while one bu ...

  9. Ping Pong Buffer 双缓冲 C++代码学习

    1.Ping Pong Buffer 原理分析 基本原理如上图所示,当设备有数据来时,先放入缓冲区1 然后将缓冲区1的数据放入缓冲区2,这时缓冲区1可接收下次数据.工作区可从缓冲区2拿数据 2.C++ ...

  10. 心跳PING PONG用法

    写PHP代码十多年了,没有接触过这个概念.只是知道MySQL有PING. 后来用其他语言才偶然接触到心跳的规则,请求参数发送PING,返回内容为PONG表示这个服务可用,算是一个不成文的规范.

最新文章

  1. 这个Python知识点,90%初学者没太整明白
  2. 重磅直播|嵌入式开发漫漫之路—从小白到技术骨干
  3. JS基础知识学习(一)
  4. 运行cmd直接进入指定目录下的命令
  5. QN8027性能调试
  6. [ASP.NET 控件实作 Day8] 控件常用 Attribute 介绍(1)
  7. C++ 11 创建和使用共享 weak_ptr
  8. deb包如何改支持12系统_对一个deb包的解压、修改、重新打包全过程方法
  9. 什么是ajax?ajax作用是什么?
  10. idea svn切换分支,合并分支
  11. 基于SpringBoot进销存ERP管理系统,源代码分享
  12. sumif单列求和_EXCEL条件求和函数SUMIF的几种常见用法
  13. 黑苹果,Win7,Win10,Xp 各个系统镜像文件下载地址(备用)
  14. 【TINY4412】U-BOOT移植笔记:(9)SD卡启动U-BOOT
  15. 有些jpg图在IE浏览器中打不开
  16. compiz的ubuntu10.04安装
  17. 【学习笔记】爬虫框架Scrapy入门
  18. 网络邻居看不到其他计算机,在网上邻居中看不到自己的电脑也看不到别人的解决方法...
  19. vulnhub靶场——Hacker-Kid-v1-0-1
  20. SpringMVC框架个人笔记之响应数据、文件上传

热门文章

  1. poj3537 Crosses ans Crosses
  2. c语言编程解百马百瓦古题,java编程题90道.doc
  3. Building your Deep Neural Network - Step by Step v5 作业 - Neural Networks and Deep Learning
  4. Python 根据出生日期判断星座
  5. 基于OPENCV的图形识别系统
  6. 软考网络工程师好考吗?怎么备考?
  7. ArcEngine修改像素值与像元值
  8. 技术分享 | Online DDL 工具 gh-ost
  9. 什么是gzip?为什么要用gzip?使用gzip的优势和劣势是什么?需要哪些岗位进行配合才能够实现gzip内容的应用?
  10. 用户DSN、系统DSN、文件DSN的区别