處理場景:三臺線陣相機或者多臺其他傳感器與工控機連著,通過調用相機的SDK控制相機,例如同時開關,同時從相機中獲取數據。然后采集軟件接收到相機數據后,將數據保存到硬盤。
應用場景
處理的問題:如果三臺相機發送數據頻率很快,并且數據量很大的話,如何快速將數據寫入到硬盤里或者對數據的其他處理,從而保證內存不增長?
解決方案簡述:每收到一坨相機的數據,就將其從尾部壓入隊列;同時有另外一個線程在等著處理這個隊列,只要這個隊列有數據,就從頭部彈出一個數據,然后對這個數據進行相應的處理。這里存在一個問題:如果相機頻率太快,也就是說隊列會漲得非常快,漲數據的速度大于處理數據的速度,內存就會上漲,直至崩潰。處理數據,例如將相機數據保存到硬盤,如果硬盤寫數據的速度很慢,就會出現內存漲爆的情況,這個時候可以考慮用多線程進行處理,從隊列彈出一個數據后,馬上被一個線程接上,這樣可以顯著提升CPU的利用率,加快數據處理速度。
隊列緩存
簡要代碼如下:
需特別注意代碼中的注釋。
//頭文件代碼
#include <iostream>
#include <windows.h>
#include <queue>
#include <mutex>
#include <future>
#include "SThreadPool.h"
struct CameraData
{
int height;
int width;
int datasize;
std::unique_ptr<char[]> data;
UINT64 timestamps;
};
class Sensor
{
public:
Sensor();
~Sensor();
//**獲取相機數據的回調,并在回調中將相機數據壓入容器中**//
friend int WINAPI s_ManagaGetImageCallBack(void* _context, void* _system, void* _data);
//從隊列頭部彈出數據并進行處理//
void t_ProcessDataQueue(void* para);
void SaveImage(std::shared_ptr<CameraData> data);
friend void t_SaveCameraImage(void* para, std::shared_ptr<CameraData> data);
private:
//用來存儲三臺相機數據的容器//
std::mutex m_DataMtx_;
std::queue<std::shared_ptr<CameraData>> m_QueCamData_;
std::condition_variable m_DataVar_;
bool m_bQueExit_ = false;
std::shared_ptr<SThreadPool> m_Pool_;
std::future<void> m_fuProcessDataQue_;
};
實現文件中,重點關注函數t_ProcessDataQueue(void* para)
;同時這段代碼還包括一種有效退出類內線程的方式,請看析構函數部分。
//實現文件
Sensor::Sensor()
{
m_fuProcessDataQue_ = std::async(std::launch::async, &Sensor::t_ProcessDataQueue, this, nullptr);
unsigned long hardwares = std::thread::hardware_concurrency();
m_Pool_ = std::make_shared<SThreadPool>(hardwares);
}
Sensor::~Sensor()
{
{
std::unique_lock<std::mutex> locker(m_DataMtx_);
m_bQueExit_ = true;
}
m_DataVar_.notify_one();
m_fuProcessDataQue_.get();
}
int WINAPI s_ManagaGetImageCallBack(void* _context, void* _system, void* _data)
{
Sensor* pThis = (Sensor*)_context;
std::shared_ptr<CameraData> data = std::make_shared<CameraData>();
//將從相機返回出來的數據組裝成CameraData.....
{
std::unique_lock<std::mutex> lock(pThis->m_DataMtx_);
pThis->m_QueCamData_.push(data);
}
pThis->m_DataVar_.notify_one();//通知另外一個線程進行數據的處理
return 0;
}
void Sensor::t_ProcessDataQueue(void* para)
{
std::shared_ptr<CameraData> tempdata;
std::unique_lock<std::mutex> locker(m_DataMtx_);
while (true)
{
try
{
if (!m_bQueExit_) m_DataVar_.wait(locker);
locker.unlock();
while (true)
{
locker.lock();
if (m_QueCamData_.empty())
{
std::cout << "camera set queue is empty.\n";
break;
}
else
{
tempdata = m_QueCamData_.front();
m_QueCamData_.pop();
}
locker.unlock();
//process camera data//
//01 單線程方式存數據
SaveImage(tempdata);
//02 多線程的方式存數據//
m_Pool_->enqueue(t_SaveCameraImage, this, tempdata);
}
}
catch (const std::exception& e)
{
std::cout << e.what() << "\n";
}
if (m_bQueExit_)
{
break;
}
}
}
void Sensor::SaveImage(std::shared_ptr<CameraData> data)
{
//保存相機
//do want you want
}
void t_SaveCameraImage(void* para, std::shared_ptr<CameraData> data)
{
//保存相機數據
//do want you want
}
作為數據采集程序,這段代碼非常適用。任何傳感器的數據我都能用一個容器先接著,然后另外一個線程處理從這個容器中彈出來的數據。如果采用回調的方式獲取傳感器數據,這種方式還不堵塞回調。既然作為一種通用的方式,這段代碼當然可以用模板類進行改進。筆者嘗試著寫了一個模板類,目前測試有效,下一篇文章公布模板代碼。
歡迎大牛們與我進行交流探討,如果對上述代碼有疑問或者缺少某個頭文件(如SThreadPool,這是一個線程池模板類),請掃碼加我微信,備注“C++”。
WOODS