简体   繁体   中英

Why a waiting thread does not wake up after calling notify?

I am trying to simulate a sensor that outputs data at a certain frame rate while another is waiting to have a data ready and when it is ready it copies it locally and processes it.

Sensor sensor(1,1000);
Monitor monitor;

// Function that continuously reads data from sensor
void runSensor()
{
    // Initial delay
    std::this_thread::sleep_for(std::chrono::milliseconds(2000));

    for(int i = 0; i < SIZE_LOOP; i++)
    {
        monitor.captureData<Sensor>(sensor, &Sensor::captureData);
    }
}

// Function that waits until sensor data is ready
void waitSensor()
{
    monitor.saveData<Sensor>(sensor, &Sensor::saveData);
}

// Main function
int main()
{
    // Threads that reads at some frame rate data from sensor
    std::thread threadRunSensor(runSensor);

    // Processing loop
    for(int i = 0; i < SIZE_LOOP; i++)
    {
        // Wait until data from sensor is ready
        std::thread threadWaitSensor(waitSensor);

        // Wait until data is copied
        threadWaitSensor.join();

        // Process synchronized data while sensor are throwing new data
        std::cout << "Init processing (" << sensor.getData() << /*"," << sensor2.getData() << */")"<< std::endl;
        // Sleep to simulate processing load
        std::this_thread::sleep_for(std::chrono::milliseconds(10000 + (rand() % 1000)));
        //std::this_thread::sleep_for(std::chrono::milliseconds(500));
        std::cout << "End processing" << std::endl;
    }

    return 0;
}

This is the sensor class. It has two methods. One that generates the data and other that copies the data locally.

class Sensor
{
    private:

    int counter;
    int id;
    int frameRate;
    int dataCaptured;
    int dataSaved;

    public:

    Sensor(int f_id, int f_frameRate)
    {
        id = f_id;
        counter = 0;
        frameRate = f_frameRate;
    };

    ~Sensor(){};

    void captureData()
    {
        dataCaptured = counter;
        counter ++;
        std::cout << "Sensor" << id << " (" << dataCaptured << ")"<< std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(frameRate + (rand() % 500)));
    };

    void saveData()
    {
        dataSaved = dataCaptured;
        std::cout << "Copying sensor" << id << " (" << dataSaved << ")"<< std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1 + (rand() % 5)));
    }

    int getData()
    {
        return dataSaved;
    }
};

Then there is a class Monitor that ensures these operations are protected to concurrent accesses.

#include <iostream> 
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <cstdlib>

#define SIZE_LOOP 1000

class Monitor
{
private:

    std::mutex m_mutex;
    std::condition_variable m_condVar;
    bool m_isReady;

public:

    Monitor()
    {
        init();
    };

    ~Monitor()
    {
    };

    void init() 
    {
        m_isReady = false;
    };

    template<class T>
    void captureData(T& objectCaptured, void (T::* f_captureFunction_p)())
    {
        // Lock read
        std::unique_lock<std::mutex> lock = std::unique_lock<std::mutex>(m_mutex);
        (objectCaptured.*f_captureFunction_p)();
        m_isReady = true;
        m_condVar.notify_one();
        lock.unlock();
    };

    template<class T>
    void saveData(T& objectSaved, void(T::*f_saveFunction_p)())
    {
        std::unique_lock<std::mutex> lock = std::unique_lock<std::mutex>(m_mutex);
        while(!m_isReady)
        {
            m_condVar.wait(lock);
        }
        (objectSaved.*f_saveFunction_p)();
        m_isReady = false;
        lock.unlock();
    };
};

Can anyone tell me why the waiting thread does not wakeup if the sensor is notifyng every frame rate?

The idea is having two threads with this workflow:

  • ThreadCapture captures a data consinuously notifying to ThreadProcessing when the data capture is done.
  • ThreadCapture must waits to capture a new data only if the current captured data is being copied on ThreadProcessing.
  • ThreadProcessing waits to a new captured data, makes a local copy, notifies to ThreadCapture that the copy is done and process the data.
  • The local copy is made on ThreadProcessing to allow ThreadCapture can capture new data while ThreadProcessing is processing.

Finally I found the solution adding a waiting step after the capture to give time to save data

template<class T>
void captureData(T& objectCaptured, void (T::* f_captureFunction_p)())
{
    std::unique_lock<std::mutex> lockReady = std::unique_lock<std::mutex>(m_mutexReady, std::defer_lock);
    std::unique_lock<std::mutex> lockProcess = std::unique_lock<std::mutex>(m_mutexProcess, std::defer_lock);

    // Lock, capture, set data ready flag, unlock and notify
    lockReady.lock();
    (objectCaptured.*f_captureFunction_p)();
    m_isReady = true;
    lockReady.unlock();
    m_conditionVariable.notify_one();
    
    // Wait while data is ready and it is not being processed
    lockReady.lock();
    lockProcess.lock();
    while(m_isReady && !m_isProcessing)
    {
        lockProcess.unlock();
        m_conditionVariable.wait(lockReady);
        lockProcess.lock();
    }
    lockProcess.unlock();
    lockReady.unlock();
};

template<class T>
void saveData(T& objectSaved, void(T::*f_saveFunction_p)())
{
    std::unique_lock<std::mutex> lockReady(m_mutexReady, std::defer_lock);
    std::unique_lock<std::mutex> lockProcess(m_mutexProcess, std::defer_lock);

    // Reset processing
    lockProcess.lock();
    m_isProcessing = false;
    lockProcess.unlock();

    // Wait until data is ready
    lockReady.lock();
    while(!m_isReady)
    {
        m_conditionVariable.wait(lockReady);
    }

    // Make a copy of the data, reset ready flag, unlock and notify
    (objectSaved.*f_saveFunction_p)();
    m_isReady = false;
    lockReady.unlock();
    m_conditionVariable.notify_one();

    // Set processing
    lockProcess.lock();
    m_isProcessing = true;
    lockProcess.unlock();
};
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM