一、背景
通过USB3.0获取数据,速度可达30帧,获取数据后解析处理,将处理后的数据保存在磁盘中,如果保存操作放置于生产所在线程,则会影响数据的获取,进而导致帧率变慢,所以要保证生产消费平衡,即消费的操作在另外的线程执行,且不能够导致生产阻塞。
二、验证
数据保存在磁盘所需的时间,记录每一帧写入的时间以及1000帧所需时间,不同的磁盘数据如下:
处理后的数据需要经过压缩,压缩耗时90多ms,如果要确保帧率,则需要消费线程能够及时消费完成,不能阻塞到生产线程。
三、方案
思路:启用三种线程来实现,线程1:产生数据,并将数据拷贝后启动线程2;线程2为工作线程(压缩工作);线程3:负责通过文件映射写入压缩后的数据;
具体实现如下:
1.使用定时器模拟获取数据,在定时器中断函数中拷贝数据,同时启动压缩工作线程,压缩工作线程放置于线程池中;
2.压缩工作线程获得帧序和数据,以及数据大小,压缩后将压缩后的数据,大小和帧序组合插入到保存线程的Map中。
3.保存线程按帧序查找Map中已经压缩好的帧后数据进行保存,保存后从Map中移除。
关键代码如下:
线程1:
qint64 size = m_pointCountPerLine * m_lineCountPerFrame;
int *InputBuffer = new int[size];
memcpy(InputBuffer, m_pInputBuffer, m_frameSizeByte);
ZlibWorker *worker = new ZlibWorker(m_frameIndex, m_frameSizeByte, InputBuffer,&m_frameBufferMap);
QObject::connect(worker, &ZlibWorker::finished,
this, &FileMapWrite::SlotZlibFinished, Qt::QueuedConnection);
m_frameIndex++;
QThreadPool::globalInstance()->start(worker);
ui.m_frameCountLbl->setText(QString::number(m_frameIndex));
线程2:
void ZlibWorker::run()
{
if (m_pFrameDataBuffer == NULL)
{
return;
}
unsigned long afterCompresssize = compressBound(m_frameByteSize);
unsigned char *desbuf = new unsigned char[afterCompresssize];
unsigned long deslenth = afterCompresssize;
unsigned char* sourceBuf = (unsigned char*)m_pFrameDataBuffer;
int rlt = compress2(desbuf, &deslenth, sourceBuf, m_frameByteSize, m_compressLevel);
if (Z_OK ==rlt)
{
tCompressFrame *frameInfo = new tCompressFrame;
frameInfo->ready = true;
frameInfo->buffer = desbuf;
frameInfo->byteSize = deslenth;
frameInfo->frameIndex = m_frameIndex;
m_mutex.lock();
m_pFrameBufferMap->insert(m_frameIndex, frameInfo);
m_mutex.unlock();
}
else {
m_pFrameBufferMap->insert(m_frameIndex, NULL);
}
if (m_notify == false)
{
m_notify = true;
emit finished(m_frameIndex, NULL);
}
delete[]m_pFrameDataBuffer;
m_pFrameDataBuffer = NULL;
}
线程3:
do
{
m_mutex.lock();
auto itor = m_frameBufferMap.find(m_curSaveFrameIndex);
m_mutex.unlock();
if (itor != m_frameBufferMap.end())
{
tCompressFrame *frameInfo = itor.value();
if (frameInfo)
{
m_fileSizeByte += frameInfo->byteSize + sizeof(int);
DWORD hignSize = 0;
DWORD lowValue = 0;
unsigned long long fileSizeByte = m_fileSizeByte;
lowValue = fileSizeByte;
hignSize = fileSizeByte >> 32;
m_mapFile = CreateFileMapping(m_file, NULL, PAGE_READWRITE, hignSize, lowValue, LPCWCHAR("Resource"));
m_openMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, true, LPCWCHAR("Resource"));
m_mapFilePtr = static_cast<int*>(MapViewOfFile(m_openMap, FILE_MAP_ALL_ACCESS, 0, 0, 0));
int*mapLength = m_mapFilePtr + 1;
unsigned char *mapData = (unsigned char*)m_mapFilePtr + sizeof(int);
memcpy(mapLength, &frameInfo->byteSize, sizeof(int));
memcpy(mapData, frameInfo->buffer, frameInfo->byteSize);
delete[]frameInfo->buffer;
UnmapViewOfFile(m_mapFilePtr);
CloseHandle(m_openMap);
CloseHandle(m_mapFile);
m_frameBufferMap.erase(itor);
}
m_curSaveFrameIndex++;
emit finished(m_curSaveFrameIndex);
}
else {
Sleep(10);
}
}while ((!m_stopRecord)||(m_curSaveFrameIndex < m_frameIndex));
CloseMapResource();
注意:线程3启动的时间要在线程2处理一帧后才开始启动;
线程2中的插入Map操作需要加锁,不然会导致Map中Find不到情况,