填充并保存线程之间的共享缓冲区

时间:2019-06-18 14:11:51

标签: c++ multithreading mutex circular-buffer

我正在使用检索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

1 个答案:

答案 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()停止处理。