双缓冲学习[已迁移]

转自:https://gpp.tkchu.me/double-buffer.html,chatgpt,https://blog.51cto.com/u_15214399/4914060

1.介绍

定义缓冲类封装了缓冲:一段可改变的状态。 这个缓冲被增量地修改,但我们想要外部的代码将修改视为单一的原子操作。 为了实现这点,类保存了两个缓冲的实例:下一缓冲当前缓冲。 以下情况都满足时,使用这个模式就很恰当:

  • 我们需要维护一些被增量修改的状态。
  • 在修改到一半的时候,状态可能会被外部请求。
  • 我们想要防止请求状态的外部代码知道内部的工作方式。
  • 我们想要读取状态,而且不想等着修改完成。

关键点:

  1. 在状态被修改后,双缓冲需要一个swap步骤。 交换缓冲区的指针或者引用,速度快。 不管缓冲区有多大,交换都只需赋值一对指针。
  2. 这个模式的另一个结果是增加了内存的使用。 正如其名,这个模式需要你在内存中一直保留两个状态的拷贝。 在内存受限的设备上,你可能要付出惨痛的代价。 如果你不能接受使用两份内存,你需要使用别的方法保证状态在修改时不会被请求。

双缓冲解决的核心问题状态有可能在被修改的同时被请求 

2.例子

应用程序向磁盘写入日志,引入双缓冲区机制,一个缓冲区存储应用程序端发送的日志,按照时间顺序依次存储;另一个缓冲区负责向低层磁盘发送写文件请求。双缓冲区的奇妙之处就在于,两个缓冲区的交换,是通过交换指针来实现的,非常的高效。

// 缓冲区1: 负责接收应用程序发来的日志
LinkedList<String> currentBuffer = new LinkedList<>();
 
// 缓冲区2: 负责将数据同步到磁盘
LinkedList<String> syncBuffer = new LinkedList<>();

// 应用程序写日志,写入第一个缓冲区
public void log(String content) {
    // 加锁保证第一个缓冲区
    synchronized(this) {
        // 将log写入内存缓冲中,这里不会直接刷入磁盘文件
        currentBuffer.add(content);
    }
 
    // 将缓冲区中的内容刷到磁盘
    logSync();
}

// 第二缓冲区,向磁盘写日志,并在写入后交换缓冲区指针
private void logSync() {
    synchronized(this) {
        // 当前在刷内存缓冲到磁盘中去
        if (isSyncRunning) {
            // 判断是否第二个缓冲区还在刷
            while (isSyncRunning) {
                try {
                    // 释放锁,即允许第一个缓冲区继续接收日志缓存, 然后等待被唤醒
                    wait(2000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            // 此时没有人在写磁盘
        }
 
        // 交换缓冲区指针
        setReadyToSync();
 
        // 设置当前正在同步到磁盘的标志位
        isSyncRunning = true;
    }
 
    // 刷磁盘,性能最低,不能加锁
    logBuffer.flush();
 
    synchronized(this) {
        // 同步完磁盘之后,将标志位复位
        isSyncRunning = false;
        // 唤醒其他等待刷磁盘的线程
        notifyAll();
    }
}

public void setReadyToSync() {
    LinkedList<String> tmp = currentBuffer;
    currentBuffer = syncBuffer;
    syncBuffer = tmp;
}

两个缓冲区各自处理,互不干扰

两个缓冲区很好的解决了应用程序的“快速、多线程”与IO操作的“缓慢,单线程”的矛盾。应该说,引入双缓冲区是一个显而易见的方式。

 

posted @ 2024-06-23 01:23  lypbendlf  阅读(8)  评论(0编辑  收藏  举报