重温一下读写双缓冲问题

好久没写过双缓存了,趁现在有空重新温习下。

我们经常听说双缓存,但是很少使用多缓存,起码大多数情况下是这样吧。为什么不需要多缓冲呢,今天分析下。并不是缓冲区越多越好,这个需要考虑具体的应用场景。我们抽象假设一下应用场景,为了简化场景,假设只有一个读线程和一个写线程,设读时间为rt,写时间为wt,有三种情况:

1、当 rt==wt时,也就是说,读时间等于写时间。这时候,开辟几个缓冲区好呢,应该是两个。看以下时间图(图画得水,看得懂就好)

重上面的图可以看出,从写1开始,写1完成后,读1开始同时写2,当读1完成时写2正好也完成,因此理论上,这重情况下使用双缓存就可以了。

2、当rt>wt时,即读快于写,也就是读的时间小于写的时间,那么这时候应该使用几个缓存呢?理论上应该不超过两个,看以下时间图

写的时间比读的长,写1开始,写1完成后,读1开始时同时开始写2。当读1完成时,写2还没写完,所以这时候,即使有再多的缓存也没用(这里不考虑多线程写),所以最多有两个缓存就够了。为了搞高性能,这里最好使用多线程写,当然了,要是多核cpu。

3、当rt<wt时,即写快于读,这时候理论上应该设置2到3个缓存区就够了。看图

这个就不解释了,因为前面有,都类似,读得慢,写的再快也没有多大意义(除了占空间)。

有考虑不到的情景,请多多指教,谢谢!上个代码:代码里对_read_list和_write_list进行上锁操作,只是为了同时满足那三种时间关系。若已确定了是哪两种模型,可以去掉锁采用更快的方法

char buffer1[1024];
char buffer2[1024];

std::vector<char*> _buffer_list;
std::vector<int> _read_list; // 可读缓存下标集合
std::vector<int> _write_list;// 可写缓存下标集合
std::mutex       _mutex;     // 同步锁
std::atomic<bool> _stopflag(false); // 全局停工标志

void thread_read(Event* _er,Event* _ew)
{
    while(!_stopflag)
    {
        // 等待读
        if (_er->wait_for(std::chrono::milliseconds(2000)))
        {
            while(true)
            {
                // 检查可读缓存的下标集合
                int idx = -1;
                _mutex.lock();
                if (!_read_list.empty())
                {
                    idx = *_read_list.begin();
                    _read_list.erase(_read_list.begin());
                }
                _mutex.unlock();

                if (idx==-1)
                {
                    break;
                }

                // 进行写
                char* pbuffer = _buffer_list[idx];
                cout << pbuffer << endl;
                // 模拟读很慢
                //Sleep(500);

                // 加入可写,上锁
                _mutex.lock();
                _write_list.push_back(idx);
                _mutex.unlock();

                // 通知可写
                _ew->notify_all();
            }
        }

        // do other
    }
}

void thread_write(Event* _er,Event* _ew)
{
    int global = 0;
    while(!_stopflag)
    {
        // 等待写
        if (_ew->wait_for(std::chrono::milliseconds(2000)))
        {
            while(true)
            {
                // 检查可写缓存的下标集合
                int idx = -1;
                _mutex.lock();
                if (!_write_list.empty())
                {
                    idx = *_write_list.begin();
                    _write_list.erase(_write_list.begin());
                }
                _mutex.unlock();

                if (idx==-1)
                    break;

                // 进行写
                char* pbuffer = _buffer_list[idx];
                memset(pbuffer,0,1024);
                sprintf(pbuffer,
                    "this is threadid %i write %i buffer %i times",
                    std::this_thread::get_id().hash(),
                    idx,
                    ++global);

                // 加入可读
                _mutex.lock();
                _read_list.push_back(idx);
                _mutex.unlock();

                // 通知可读
                _er->notify_all();
            }
        }

        // do other
    }
}

int main()
{
    _buffer_list.push_back(buffer1);
    _buffer_list.push_back(buffer2);

    Event event_read,event_write;

    std::list<std::thread> _list_thr;
    // 读线程
    _list_thr.push_back(std::thread(thread_read,&event_read,&event_write));
    // 写线程
    _list_thr.push_back(std::thread(thread_write,&event_read,&event_write));

    system("pause");
    // 开始时,全部缓存可写
    for (size_t i=0; i<_buffer_list.size(); ++i)
        _write_list.push_back(i);

    //通知写
    event_write.notify_once();

    system("pause");
    _stopflag = true;

    for (auto& thr : _list_thr)
        thr.join();

    return 0;
}
时间: 2024-10-05 23:27:22

重温一下读写双缓冲问题的相关文章

服务器应用--双缓冲队列

在服务器开发中 通常的做法是 把 逻辑处理线程和I/O处理线程分离. 逻辑处理线程:对接收的包进行逻辑处理. I/0处理线程:网络数据的发送和接收,连接的建立和维护. 通常 逻辑处理线程和I/O处理线程是通过数据队列来交换数据,就是生产者--消费者模型. 这个数据队列是多个线程在共享,每次访问都需要加锁,因此如何减少 互斥/同步的开销就显得尤为重要.                                                                         解

avalon与双缓冲技术

avalon与双缓冲技术 avalon1.5一个重要技术升级是引进异步渲染.异步渲染在游戏界有一个更专业的名字,叫双缓冲.游戏界要刷新界面与我们刷新浏览器视图,面临的问题是一致的.视图是由许多存在套嵌关系的方块组成,它们每一个的改动,都可能引起reflow(其父节点,其父父节点的大小重新计算),这是造成性能问题的关键. 双缓冲技术的主要原理是:当一个动画争先显示时,程序又在改变它,前面的画面还没显示完,程序又要求重新绘制,这样屏幕就会不停闪烁.为了避免闪烁,可以使用双缓冲技术,将要处理的图片都放

MFC利用双缓冲刷新绘图

在VC中进行绘图过程处理时,如果图形刷新很快, 经常出现图形闪烁的现象.利用先在内存绘制,然后 拷贝到屏幕的办法可以消除屏幕闪烁,具体的方法是先在内存 中创建一个与设备兼容的内存设备上下文,也就是开辟一快内 存区来作为显示区域,然后在这个内存区进行绘制图形.在绘制完成后利用 BitBlt函数把内存的图形直接拷贝到屏幕上即可. 具体想实现的是: 在Dialog客户区的一个图片控件(IDC_MAP)中绘制几个动态的点,如果不用双缓冲的技术,在屏幕刷新的时候会有闪烁的现象. CRect rect; C

OpenGL中实现双缓冲技术

在OpenGL中实现双缓冲技术的一种简单方法: 1.在调用glutInitDisplayMode函数时, 开启GLUT_DOUBLE,即glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);.这里将我们惯用的GLUT_SINGLE替换为GLUT_DOUBLE,意为要使用双缓冲而非单缓冲. 2. 调用glutDisplayFunc(display)注册回调函数时, 在回调函数中所有绘制操作完成后调用glutSwapBuffers()交换两个缓冲区指针. 3. 调用

OpenGL的消隐与双缓冲

首先是大家可能已经发现,在我们之前提到的所有例子中,在图形的旋转过程中整个图形都有一定程度的闪烁现象,显得图形的过渡极不平滑,这当然不是我们所要的效果,幸好opengl 支 持一个称为双缓存的技术,可以有效的帮助我们解决这个问题.我们知道在我们电脑中,屏幕中显示的东西都会被放在一个称为显示缓存的地方,在通常情况下我们 只有一个这样的缓冲区,也就是单缓冲,在单缓冲中任何绘图的过程都会被显示在屏幕中,这也就是我们为什么会看到闪烁,而所谓双缓冲就是再这个显示的缓冲区 之外 再建立一个不显示的缓冲区,我

GDI双缓冲绘图

一.简介 在进行复杂图形绘制时,若直接在屏幕DC上进行绘制,则会出现明显的闪烁.闪烁产生的原因是当绘制的图形较为 复杂时,图形绘制过程中就被刷新到屏幕上,导致结果断断续续地显示出来.双缓冲绘图的原理是在另开辟一块内存用于绘制,当所有绘制工作完成后将内存数据一 次性拷贝到屏幕上. 双缓冲绘图步骤: 创建兼容DC(CreateCompatibleDC) 创建兼容位图(CreateCompatibleBitmap) 将兼容位图选入兼容DC(SelectObject) 在兼容DC中进行绘制工作 将兼容D

MFC双缓冲绘图解决界面闪烁问题

一:为什么会产生界面闪烁? 解释这个之前,我们需要明白的是在MFC里面绘图的消息响应机制,大概的就是如果我们要在某一个 东西上面绘图,比如对话框,单文档等等,就必须先得到图形DC的句柄(handle),然后在指定句柄的基础上进行图形操作,也就是MFC常用的CDC *DC = this->getDC();其中的this就是你想画图的目标. MFC里在消息响应的过程中,WM_PAINT被转变为OnDraw()(单文档 Single Document)或是OnPaint()(对 话框Dialog)之类

位图操作和双缓冲机制

位图操作代码部分: CRect rect;  GetClientRect(rect);  pDC->SetMapMode(MM_ANISOTROPIC);  pDC->SetWindowExt(rect.Width(), rect.Height());  pDC->SetViewportExt(rect.Width(), -rect.Height());  pDC->SetViewportOrg(rect.Width()/2, rect.Height()/2); CDC MemDC

c++双缓冲技术绘图避免闪烁

当数据量很大时,绘图可能需要几秒钟甚至更长的时间,而且有时还会出现闪烁现象,为了解决这些问题,可采用双缓冲技术来绘图. 双缓冲即在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,这样能大大加快绘图的速度.双缓冲实现过程如下: 1.在内存中创建与画布一致的缓冲区 2.在缓冲区画图 3.将缓冲区位图拷贝到当前画布上 4.释放内存缓冲区 在图形图象处理编程过程中,双缓冲是一种基本的技术.我们知道,如果窗体在响应WM_PAINT消息的时候