samedi 28 octobre 2017

Relatively Efficient Timer?

Is there a significant/noteworthy downside to spawning a "junk thread" for use in a timer?

I mean this in the sense that instead of spamming calls to std::chrono::someclocktype::now(), generating a thread to only handle a mutex and condition_variable. I have provided a super basic example of such a thing. It was thrown together while on a lunch break, so please excuse the mess.

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

class Timer
{
    public:
        Timer(uint32_t durationInMS) 
        { 
            m_duration = std::chrono::milliseconds(durationInMS); 
            m_thread = new std::thread(startTimer, this);
        }
        ~Timer()
        {
            m_done = true;
            while(!m_thread->joinable()) { m_cv.notify_all(); }
            m_thread->join(); 
            delete m_thread; 
            m_thread = nullptr;
        }
        static void startTimer(Timer* t) { t->start(); } // thread starter
        bool expired() { return m_done; }       
        void start()
        {
            m_end = std::chrono::_V2::steady_clock::now() + m_duration; // get end point
            std::mutex timeMutex; // 
            std::unique_lock<std::mutex> ulock(timeMutex); // create mutex and lock

            // loop
            while(!m_done)
            {
                if(m_cv.wait_for(ulock, m_interval, [&] { return (std::chrono::_V2::steady_clock::now() > m_end); } ))
                {
                    // double check to account for spurious wake-ups
                    if(std::chrono::_V2::steady_clock::now() > m_end) 
                    { 
                        m_done = true; 
                        ulock.unlock();
                    }
                }
            }
        }


    private:
        bool m_done = false;
        std::chrono::time_point<std::chrono::_V2::steady_clock> m_end;
        std::chrono::milliseconds m_duration = std::chrono::milliseconds(0);
        std::chrono::milliseconds m_interval = std::chrono::milliseconds(200);
        std::thread* m_thread = nullptr;
        std::condition_variable m_cv;
};


int main()
{
    Timer* t = new Timer(3000);
    std::cout<<"Started Timer!\n";  

    // inefficient, example loop
    while(!t->expired()) { /*std::cout<<"Not quite dead yet\n";*/ }

    // cleanup and bail
    std::cout<<"Finally done here...\n";
    delete t; t = nullptr;

    return 0;
}

It does compile and it does work with a 3 second "pause" before printing the file line and closing. As commented in the example, I know the final while loop is horribly inefficient, but I needed some simplistic way to test.

Aucun commentaire:

Enregistrer un commentaire