2

I'm learning C++ since yesterday and I'd like to code an alarm system that lets the user set his own alarms, with an Alarm object. What I attempted works right in itself, but only lets the user run one: If I try to use the start method to start a second alarm, the program waits for the first alarm to "ring" to start the second one.

How can I run two alarms at the same time ? Thanks for taking time reading this. (OSX Sierra, Xcode 8.2)

main.cpp

using namespace std;

int main(int argc, const char * argv[]) {
    Alarm test(12, 12, "foo");
    test.start();

Alarm.hpp

class Alarm {
    public:
    Alarm(int hour, int minute, std::string speech);
    void start();
    void stop();
    bool isStopped() const;
    std::string getStats() const;

    private:
    int m_hour;
    int m_minute;
    std::string m_speech;
    bool m_stopped;
};

Alarm.cpp

using namespace std;

Alarm::Alarm(int hour, int minute, string speech) {
    m_hour = hour;
    m_minute = minute;
    m_speech = speech;
    m_stopped = false;
}

void Alarm::start() {
    int currentHour, currentMinute;

    while (!Alarm::isStopped()) {
        time_t now = time(NULL);
        struct tm *current = localtime(&now);

        currentHour = current->tm_hour;
        currentMinute = current->tm_min;

        if (currentHour == m_hour && currentMinute == m_minute) {
            cout << m_speech << endl;
            m_stopped = true;
        }
        else {
           this_thread::sleep_for(chrono::milliseconds(60000));
        }
     }
}    

void Alarm::stop() {
    m_stopped = true;
}

bool Alarm::isStopped() const {
    return m_stopped;
}
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055

3 Answers3

1

The simplest way I could think of is something like using std::threads:

int main(int argc, const char * argv[]) {        
    Alarm test1(12, 12, "foo");
    Alarm test2(12, 12, "foo");

    std::thread t1(&Alarm::start,test1);
    std::thread t2(&Alarm::start,test2);

    while(!test1.isStopped() && !test2.isStopped()) {
         this_thread::sleep_for(chrono::milliseconds(60000));
    }

    t2.join();
    t1.join();
}

and protect the m_stopped member variable with a std::mutex, std::atomic or similar:

class Alarm {
    public:
    Alarm(int hour, int minute, std::string speech);
    Alarm(const Alarm& other);
    Alarm& operator=(const Alarm& other);
    void start();
    void stop();
    bool isStopped() const;
//    std::string getStats() const;

    private:
    int m_hour;
    int m_minute;
    std::string m_speech;
    std::atomic<bool> m_stopped;
};

Alarm::Alarm(int hour, int minute, string speech) 
: m_hour(hour),m_minute(minute),m_speech(speech),m_stopped(false) {
}

Alarm::Alarm(const Alarm& other) 
: m_hour(other.m_hour),m_minute(other.m_minute),m_speech(other.m_speech),m_stopped(other.isStopped()) {
}

Alarm& Alarm::operator=(const Alarm& other) {
    m_hour = other.m_hour;
    m_minute = other.m_minute;
    m_speech = other.m_speech;
    m_stopped.store(other.isStopped());
    return *this;
}

void Alarm::start() {
    int currentHour, currentMinute;

    while (!Alarm::isStopped()) {
        time_t now = time(NULL);
        struct tm *current = localtime(&now);

        currentHour = current->tm_hour;
        currentMinute = current->tm_min;

        if (currentHour == m_hour && currentMinute == m_minute) {
            cout << m_speech << endl;
            m_stopped.store(true);
        }
        else {
           this_thread::sleep_for(chrono::milliseconds(1000));
        }
     }
}    


void Alarm::stop() {
    m_stopped.store(true);
}

bool Alarm::isStopped() const {
    return m_stopped.load();
}

Note the changes I made in the example above regarding the copy constructor and the assignment operator definitions to handle the std::atomic<bool> member correctly.


I have a compilable version here, with the right parameters it should succeed and complete in time.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
0

You need multithreading for do this, but for your case I recommand to check one oject after the other.

Ben47955
  • 1
  • 5
0

You could run each alarm in its own thread, but that may be a little advanced for someone that just started C++ - and it's overkill anyway.

The standard way of handling multiple alarms is to search through all the alarms to find the one that is going to "ring" first and sleep long enough for that one. When it fires, remove it from the list and search for the next one that is going to ring first. (Finding the first alarm is easier if you keep the alarms in a sorted container such as std::map<time_t, Alarm> or even just std::map<time_t, std::string>)

Remember to handle cases when more than one alarm needs to fire at the same time.

If you want the user to be able to interact with the system while it is running (e.g. adding new alarms, removing alarms, etc.) you will still need to multithread - but you should only ever have one thread sleeping for the next alarm, rather than one thread per alarm.

Allison Lock
  • 2,375
  • 15
  • 17
  • Actually I coded that system in Python and each alarm ran a thread, and I thought about doing the same thing in C++... But I don't really know how. Even if it's overkill, how can I run a thread through an object's method ? –  Dec 19 '16 at 19:19
  • If I can't do otherwise, I'll try that method though –  Dec 19 '16 at 19:20