我正在使用检索I / Q数据的API。调用函数bbGetIQ(m_handle, &pkt);
会填充一个缓冲区。这是用户未输入“停止”时的线程循环。 Pkt是一种结构,使用的缓冲区是pkt.iqData = &m_buffer[0];
,它是float的向量。向量的大小为5000,每次循环时,缓冲区将填充5000个值。
我想将缓冲区中的数据保存到文件中,我是在调用bbgetIQ
之后立即进行的,但是这样做很耗时,数据检索得不够快,导致API丢弃数据,以便它可以继续填充其缓冲区。
这是我的代码:
void Acquisition::recordIQ(){
int cpt = 0;
ofstream myfile;
while(1){
while (keep_running)
{
cpt++;
if(cpt < 2)
myfile.open ("/media/ssd/IQ_Data.txt");
bbGetIQ(m_handle, &pkt); //Retrieve I/Q data
//Writing content of buffer into the file.
for(int i=0; i<m_buffer.size(); i++)
myfile << m_buffer[i] << endl;
}
cpt = 0;
myfile.close();
}
}
然后我尝试仅在退出循环时才写入文件:
void Acquisition::recordIQ(){
int cpt = 0;
ofstream myfile;
int next=0;
vector<float> data;
while(1){
while ( keep_running)
{
if(keep_running == false){
myfile.open ("/media/ssd/IQ_Data.txt");
for(int i=0; i<data.size(); i++)
myfile << data[i] << endl;
myfile.close();
break;
}
cpt++;
data.resize(next + m_buffer.size());
bbGetIQ(m_handle, &pkt); //retrieve data
std::copy(m_buffer.begin(), m_buffer.end(), data.begin() + next); //copy content of the buffer into final vector
next += m_buffer.size(); //next index
}
cpt = 0;
}
}
我不再从API中丢失数据,但是问题是我受到data
向量大小的限制。例如,我不能让它整夜检索数据。
我的想法是制作2个线程。一个将检索数据,另一个将数据写入文件。这两个线程将共享一个循环缓冲区,第一个线程将填充该缓冲区,第二个线程将读取该缓冲区并将内容写入文件。由于它是一个共享缓冲区,我想我应该使用互斥体。
我是多线程和互斥的新手,所以这是个好主意吗?我真的不知道从哪里开始,以及消费者线程如何在生产者填充缓冲区的同时读取缓冲区。读取时锁定缓冲区会导致API丢失数据吗? (因为它将无法将其写入循环缓冲区中。)
编辑:由于我希望我的记录线程在后台运行,以便在记录时可以做其他事情,因此我将其分离,用户可以通过设置条件{{1}来启动记录}为真。
keep_running
答案 0 :(得分:0)
您需要使用类似这样的内容(https://en.cppreference.com/w/cpp/thread/condition_variable):
全局变量:
std::mutex m;
std::condition_variable cv;
std::vector<std::vector<float>> datas;
bool keep_running = true, start_running = false;
编写线程:
void writing_thread()
{
myfile.open ("/media/ssd/IQ_Data.txt");
while(1) {
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return keep_running && !datas.empty();});
if (!keep_running) break;
auto d = std::move(datas);
lk.unlock();
for(auto &entry : d) {
for(auto &e : entry)
myfile << e << endl;
}
}
}
发送线程:
void sending_thread() {
while(1) {
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return keep_running && start_running;});
if (!keep_running) break;
}
bbGetIQ(m_handle, &pkt); //retrieve data
std::vector<float> d = m_buffer;
{
std::lock_guard<std::mutex> lk(m);
if (!keep_running) break;
datas.push_back(std::move(d));
}
cv.notify_one();
}
}
void start() {
{
std::unique_lock<std::mutex> lk(m);
start_running = true;
}
cv.notify_all();
}
void stop() {
{
std::unique_lock<std::mutex> lk(m);
start_running = false;
}
cv.notify_all();
}
void terminate() {
{
std::unique_lock<std::mutex> lk(m);
keep_running = false;
}
cv.notify_all();
thread1.join();
thread2.join();
}
简而言之:
发送线程从其接收的任何数据中接收数据,锁定互斥体mt
并将数据移至datas
存储。然后,它使用cv
条件变量来通知正在等待的线程,有事情要做。编写线程等待条件变量发出信号,然后锁定互斥体mt
,将datas
全局变量中的数据移动到本地,然后释放互斥体并继续将刚接收到的数据写入文件。关键是使互斥锁保持最少的时间。
编辑:
要终止全部操作,您需要将keep_running
设置为false。 然后呼叫cv.notify_all()
。 然后加入所涉及的线程。顺序很重要。您需要加入线程,因为写线程可能仍在写数据。
EDIT2:
添加了延迟启动。现在创建两个线程,一个运行sending_thread
,另一个运行writing_thread
。调用start()
启用处理,调用stop()
停止处理。