0

My main function loads a monitoring class. This class calls external services to periodically get some data and report health status.

These are the task_1 and task_2 in the class below, that can have sub tasks. The tasks accumulate some values that are stored to a shared "Data" class.

So each task_N is coupled with a thread that executes, sleeps for a while and does this forever until the program stops. My basic problem is that I cannot stop the threads in the Monitor class, since they might be waiting for the timer to expire (sleep)

#include <iostream>
#include <thread>
#include <utility>
#include "Settings.hpp"
#include "Data.hpp"

class Monitors {

public:

    Monitors(uint32_t timeout1, uint32_t timeout2, Settings settings, std::shared_ptr<Data> data)
            : timeout_1(timeout1), timeout_2(timeout2), settings_(std::move(settings)), data_(std::move(data)) {}


    void start() {
        thread_1 = std::thread(&Monitors::task_1, this);
        thread_2 = std::thread(&Monitors::task_2, this);
        started_ = true;
    }

    void stop() {
        started_ = false;
        thread_1.join();
        thread_2.join();
        std::cout << "stopping threads" << std::endl;

    }

    virtual ~Monitors() {

        std::cout << "Monitor stops" << std::endl;
    }

private:

    void subtask_1_1() {
        //std::cout << "subtask_1_1 reads " << settings_.getWeb1() << std::endl;
    }

    void subtask_1_2() {
        //std::cout << "subtask_1_2" << std::endl;
        data_->setValue1(21);
    }

    void task_1() {

        while(started_) {
            subtask_1_1();
            subtask_1_2();
            std::this_thread::sleep_for(std::chrono::milliseconds(timeout_1));
            std::cout << "task1 done" << std::endl;
        }

    }

    void subtask_2_1() {
        //std::cout << "subtask_2_1" << std::endl;
    }

    void subtask_2_2() {
        //std::cout << "subtask_2_2" << std::endl;
    }

    void task_2() {

        while(started_) {
            subtask_2_1();
            subtask_2_2();
            std::this_thread::sleep_for(std::chrono::milliseconds(timeout_2));
            std::cout << "task2 done" << std::endl;
        }

    }


private:

    bool started_ {false};
    std::thread thread_1;
    std::thread thread_2;
    uint32_t timeout_1;
    uint32_t timeout_2;
    Settings settings_;
    std::shared_ptr<Data> data_;

};

The main function is here:

        auto data = std::make_shared<Data>(10,20);
        Settings set("hello", "world");
        Monitors mon(1000, 24000,set,data);
        mon.start();

        int count = 1;
        while(true) {

            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
            std::cout << data->getValue2() << " and count is " << count << std::endl;
            count++;
            if ( count == 10)
                break;
        }
        std::cout << "now I am here" << std::endl;
        mon.stop();
        return 0;

Now when I call mon.stop() the main thread stops only when the timer exprires. How can I gracefully call mon.stop() and interrupt and call the task_N?

UPDATE: Since I don't want to call std::terminate, which is the proper way to implement a monitor class in c++

Captain Nemo
  • 345
  • 2
  • 14
  • 1
    You cannot "stop a thread gracefully" in C++. There is nothing like that in the standard C++ library, for a number of reasons that have to do with how C++ works fundamentally. C++ simply does not work this way, on its own. The duplicate questions have more details and some less-preferred alternatives. – Sam Varshavchik Nov 07 '19 at 12:06
  • You cannot. To stop threads gracefully, the threads must cooperate. `sleep_for` is not cooperative. – j6t Nov 07 '19 at 12:06
  • https://en.cppreference.com/w/cpp/thread/condition_variable/wait_for could help – Mat Nov 07 '19 at 12:09
  • The duplicate is inappropriate, it show how to abort a thread, not exit gracefully. – Panagiotis Kanavos Nov 07 '19 at 12:10
  • 1
    @PanagiotisKanavos A graceful termination of thread is an abortion in which the thread cooperates. Which means your thread should operate, not being asleep and being able to check some predicate to determine if thread function should return at this particular point. There is no other way. At very least, any attempt to terminate thread would cause undefined behavior because destructors of objects it owns were not called. – Swift - Friday Pie Nov 07 '19 at 12:17
  • Built-in support for cooperative termination [will come in C++20](https://en.cppreference.com/w/cpp/thread) in the form of `stop_source` and `stop_token`. Until then, the thread needs to check a synchronization primitive like a sempahore and exit if it's signalled. Unfortunately, that's also coming in C++20. – Panagiotis Kanavos Nov 07 '19 at 12:20
  • @PanagiotisKanavos condition variable exists since C++11 though. Technically semaphore isn't a primitive from some POVs and name is confusing because different systems and architectures use the term for some thing else (e.g. for interprocess synchronization object). – Swift - Friday Pie Nov 07 '19 at 12:26
  • @Swift-FridayPie they don't do the same thing. This isn't about blocking, it's about checking the status of a signal. As for sleeping threads, that's not the best way to implement a timer – Panagiotis Kanavos Nov 07 '19 at 12:28
  • I am not getting an answer. My purpose is to create a monitor class with async tasks and I want that class to stop properly. – Captain Nemo Nov 07 '19 at 12:44
  • 1
    Generally, on stackoverflow.com you are not going to get an answer that simply says: "write the code to do it". You will have to figure out by yourself how an execution thread in your program gets notified that it should stop. There is no global, universal way to do it. Each application works differently, and this gets implemented in whatever way it works, for each application. This generally means you just can't have execution threads ever call `sleep_for`, or something similar, because that's what they do: they pause the thread, no matter what. You will have to do something else, instead. – Sam Varshavchik Nov 07 '19 at 13:19

0 Answers0