重温一下读写双缓冲问题

Posted xiaojack

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重温一下读写双缓冲问题相关的知识,希望对你有一定的参考价值。

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

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

 

以上是关于重温一下读写双缓冲问题的主要内容,如果未能解决你的问题,请参考以下文章

使用双缓冲技术进行并发读写?

GDI双缓冲的一些学习

Qt使用双缓冲绘图时报错:pure virtual method called

简单的 "双缓冲" 绘图的例子(研究一下)

opengl如何在MFC中设置双缓冲

主从复制 读写分离 原理重温