Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loop timer thread

I would like to create thread pool for loop timer: for each n seconds, we will run task.

In my example, we have two tasks. For each task, I will create one thread to run. I add the condition of status for each task, if the status is false, we do nothing. I need the status to control the timer, I can call the timer start/stop (sorry, in this code, there is only start function).

Here is my code:

 #include <iostream>
#include <list>
#include <functional>
#include <map>
#include <thread>
#include <chrono>
#include <vector>
#include <mutex>

//using namespace std::chrono_literals;

void print_time()
{
    std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
    std::chrono::system_clock::duration tp = now.time_since_epoch();
    tp -= std::chrono::duration_cast<std::chrono::seconds>(tp);
    std::time_t ttp = std::chrono::system_clock::to_time_t(now);
    tm t = *gmtime(&ttp);

    std::printf("[%04u-%02u-%02u %02u:%02u:%02u.%03u]: ", t.tm_year + 1900,
                t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec,
                static_cast<unsigned>(tp/std::chrono::milliseconds(1)));
}

typedef std::function<void()> Callback;

enum E_NUM {
    NUM_ONE = 0,
    NUM_TWO
};

class Foo {
    public:
        static Foo& getInstance()
        {
            static Foo instance;
            return instance;
        }

        void init() {
            m_mapThreadStatus[NUM_ONE] = false;
            m_mapThreadStatus[NUM_TWO] = false;
            m_mapCallback[NUM_ONE] = std::bind(&Foo::test, this);
            m_mapCallback[NUM_TWO] = std::bind(&Foo::foo_test, this);
        }

        void addTimer(E_NUM id, int num_seconds) {
            std::thread th ([id, num_seconds] () {
                    std::cout << std::this_thread::get_id() << " id: " << id << "\tseconds: " << num_seconds << std::endl;
                    while (1)
                    {
                        if ( Foo::getInstance().getStatus(id) == true)
                        {
                            Foo::getInstance().callfunctor(id);
                            std::this_thread::sleep_for(std::chrono::seconds(num_seconds));
                        }
                        else
                        {
                        }
                        //std::this_thread::sleep_for(std::chrono::seconds(num_seconds));
                    }
                });
            m_mapThreads[id] = std::move(th);
        }

        void callfunctor(E_NUM id) {
            m_mapCallback[id]();
        }

        void startTimers(E_NUM id) {    
            m_mapThreadStatus[id] = true;
            if (m_mapThreads[id].joinable())
            {
                m_mapThreads[id].join();
            }
        }

        bool getStatus(E_NUM id) { 
            return m_mapThreadStatus[id];}

    private:
        void test() {
            print_time();
            std::cout << std::this_thread::get_id() << std::endl;
            std::cout << "\033[1;31m" << __PRETTY_FUNCTION__ << "\033[0m" << std::endl;
        }
        void foo_test() {
            print_time();
            std::cout << std::this_thread::get_id() << std::endl;
            std::cout << "\033[1;32m" << __PRETTY_FUNCTION__ << "\033[0m" << std::endl;
        }
        Foo() {init();}
        std::map<E_NUM, Callback> m_mapCallback;
        std::vector<std::thread> m_threads;
        std::map<E_NUM, std::thread> m_mapThreads;
        std::map<E_NUM, bool> m_mapThreadStatus;
};

int main()
{
    Foo::getInstance().addTimer(NUM_ONE, 1);
    Foo::getInstance().addTimer(NUM_TWO, 2);
    Foo::getInstance().startTimers(NUM_ONE);
    Foo::getInstance().startTimers(NUM_TWO);
    return 0;
}

But my problem: 1. it seems that only one task run, not two as expect.

40557398533888 id: 0    seconds: 1140557390141184 id: 1 seconds: 2
[2019-05-28 11:56:49.770]: 140557398533888
void Foo::test()

[2019-05-28 11:56:50.770]: 140557398533888
void Foo::test()
[2019-05-28 11:56:51.771]: 140557398533888
void Foo::test()
[2019-05-28 11:56:52.771]: 140557398533888
void Foo::test()
[2019-05-28 11:56:53.772]: 140557398533888
void Foo::test()
  1. The classical question: when we create the std::thread, it runs immediately, right? For example, link. So, how can I improve my code to start after adding timer?

EDIT 1:

I added the function in Foo:

class Foo {
    ...
    void startTimers() {
        m_mapThreads[NUM_ONE].join();
        m_mapThreads[NUM_TWO].join();
    }
};

And I call this function at the END of main, it works like a charm. Thank you.

like image 831
GAVD Avatar asked Jul 11 '26 17:07

GAVD


1 Answers

Consider your Foo::startTimers implementation...

void startTimers (E_NUM id)
{    
    m_mapThreadStatus[id] = true;
    if (m_mapThreads[id].joinable()) {
        m_mapThreads[id].join();
    }
}

You start the thread identified by id with...

m_mapThreadStatus[id] = true;

But you then join that thread. The end result being that when you start a given thread your code will block until that thread has finished. So, with...

int main()
{
    Foo::getInstance().addTimer(NUM_ONE, 1);
    Foo::getInstance().addTimer(NUM_TWO, 2);
    Foo::getInstance().startTimers(NUM_ONE);
    Foo::getInstance().startTimers(NUM_TWO);
    return 0;
}

The line...

Foo::getInstance().startTimers(NUM_ONE);

will block until the thread associated with NUM_ONE finishes -- never in the present case -- and Foo::getInstance().startTimers(NUM_TWO) is never called.

like image 83
G.M. Avatar answered Jul 13 '26 11:07

G.M.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!