0

So I have print function that I want to execute in 5 seconds. The Problem is that I want everything else in the function. So For example if my code is:

// insert code here...
printf("(5 seconds later) Hello"); /* should be executed in 5 seconds */
printf("heya");

In the main function for example. Now here's the tricky part. While the first line should be executed in 5 seconds, the second line should be executed just like normal if the first line wasn't there at all. So the output would be:

heya
(5 seconds later) Hello

If you familiar with Cocoa or Cocoa Touch, this is exactly how the NSTimer Class works. Using C++, is there a simpler or built in way other than using a thread? If not, how would I go about doing this using multi-threading?

ManOx
  • 1,935
  • 5
  • 23
  • 37

3 Answers3

4

Using <chrono> and <thread>, you can create a pretty rudimentary, but easy one:

std::thread printLater{[] {
    std::this_thread::sleep_for(std::chrono::seconds(5));
    printf("(5 seconds later) Hello");
}};

printf("heya");

printLater.join(); //when you want to wait for the thread to complete

Another method, which Pubby points out, and has the advantage of automatically waiting for the thread to finish and not stopping if an exception is thrown, is to use std::async:

auto f = std::async(std::launch::async, [] {
    std::this_thread::sleep_for(std::chrono::seconds(5));
    printf("(5 seconds later) Hello");
});

printf("heya");

The result of std::async being stored into a variable means the call will start a new thread to run the function in. If you don't store the result, no new thread. It's one of those new gotchas in the language.

Note that it might not be precisely five seconds later when it prints, and there's no syncing of the output, so you might get interleaved output blocks (printf is atomic, so the entire output of each call would interleave) if printing more than just a single thing in each thread. Without synchronization, there's no guarantee of which statements happen when, so care should be taken if you do need to be aware of synchronization issues that can arise. For basic purposes, this should work, though.

chris
  • 60,560
  • 13
  • 143
  • 205
  • Wouldn't `async` be better due to the automatic joining? – Pubby Apr 03 '13 at 01:50
  • @Pubby, Perhaps, I'm not all that experienced with the threading utilities. I'll do a bit of quick looking. – chris Apr 03 '13 at 01:52
  • I'm not very experienced either. Also, AFAIK printf synchronizes each call to prevent interleaving. Other functions obviously require synchronization though. – Pubby Apr 03 '13 at 01:54
  • So the problem I can see with this solution though, is that I need to save the thread to a variable otherwise I cannot run this code in a function without getting a memory error. So how can I save the thread to a var? Sorry for being so noobish on this. – ManOx Apr 03 '13 at 01:55
  • @Pubby, Ah, I remembered `std::cout` interleaving my single-character outputs. That's sure confusing when trying to remember it. Also, with `std::async`, did you mean saving the result to a `std::future` (so it runs in a parallel thread) and letting it wait by itself without you having to? That seems to work the same way. – chris Apr 03 '13 at 01:57
  • @chris yes. The benefit is that you can throw exceptions and return early without having thread's dtor call terminate. – Pubby Apr 03 '13 at 02:00
  • @ManOx, Sorry for being later in asking, but may I get some clarification on what doesn't work? What sort of memory error are we talking about? – chris Apr 03 '13 at 02:06
  • @chris Sure, basically I get a "sigbart" error from the compiler when I go out of scope (when I tried to put your code in a function). – ManOx Apr 03 '13 at 02:09
  • @ManOx, I tested it with something that normally always crashes when there's undefined behaviour and got nothing. Could you post a link to the code you tried? – chris Apr 03 '13 at 02:11
  • @chris Never mind, I've figured out a different answer to the problem I've posted, check it to see if there are any potential problems that I could have. – ManOx Apr 03 '13 at 02:16
1

Here's how I've solved this problem:

#include <iostream>
#include <thread>
#include <chrono>

std::thread timerThread;

void printStatement() {
    std::this_thread::sleep_for(std::chrono::seconds(5));
    printf("(5 seconds later) Hello");
}

bool myBool() {
    timerThread = std::thread(printStatement);
    return YES;
}


int main(int argc, const char * argv[])
{
    // insert code here...
    printf("%i\n", myBool());
    printf("heya\n");
    // you could also use the async stuff, join or std::future instead of sleep()
    sleep(5); // this is only here to keep the thread from being detatched before it can even finish sleeping
    timerThread.detach();
    return 0;
}
ManOx
  • 1,935
  • 5
  • 23
  • 37
  • 1
    You can use `join()` to wait for it to finish instead of sleeping and calling `detach()`. With starting a thread and then returning, though, I'd recommend sending the caller back a `std::future`. From what I've gathered, it's meant for that sort of thing, but I'm simply not experienced enough with everything to say that 100% confidently. – chris Apr 03 '13 at 02:23
0

My snap version with boost asio and std::async. No sleeps employed.

#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/noncopyable.hpp>

#include <chrono>
#include <future>
#include <memory>
#include <iostream>

class MonotonicExecutor
    : public boost::noncopyable,
      public std::enable_shared_from_this<MonotonicExecutor> {
  typedef std::function<void()> Functor;

public:
  MonotonicExecutor(boost::posix_time::time_duration trig)
      : m_service(),
        m_serviceWork(
            std::make_shared<boost::asio::io_service::work>(m_service)),
        m_deadlineTimer(m_service), m_trigger(trig) {
    auto fut = std::async(std::launch::async, [&]() { m_service.run(); });
    fut.wait_for(std::chrono::milliseconds(1));
  }
  void executeAsync(Functor fun) {
    m_deadlineTimer.expires_from_now(m_trigger);
    m_deadlineTimer.async_wait(std::bind(&MonotonicExecutor::execute,
                                         shared_from_this(),
                                         std::placeholders::_1, fun));
  }
  void abort() { m_deadlineTimer.cancel(); }

private:
  void execute(const boost::system::error_code &err, Functor fun) {
    if (err != boost::asio::error::operation_aborted &&
        m_deadlineTimer.expires_at() <=
            boost::asio::deadline_timer::traits_type::now()) {
      m_deadlineTimer.cancel();
      fun();
      m_deadlineTimer.expires_from_now(m_trigger);
      m_deadlineTimer.async_wait(std::bind(&MonotonicExecutor::execute,
                                           shared_from_this(),
                                           std::placeholders::_1, fun));
    }
  }

private:
  boost::asio::io_service m_service;
  std::shared_ptr<boost::asio::io_service::work> m_serviceWork;
  boost::asio::deadline_timer m_deadlineTimer;
  boost::posix_time::time_duration m_trigger;
};

int main(int argc, char *argv[]) {
  auto executor =
      std::make_shared<MonotonicExecutor>(boost::posix_time::seconds(3));
  executor->executeAsync([&]() {
    std::cout << boost::posix_time::to_iso_string(
                     boost::posix_time::second_clock::local_time())
              << std::endl;
  });

  auto stop = std::chrono::system_clock::now() + std::chrono::seconds(30);
  while (std::chrono::system_clock::now() < stop) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }
  executor->abort();
  std::cout << "Wait and see if the task is aborted" << std::endl;
  stop = std::chrono::system_clock::now() + std::chrono::seconds(30);
  while (std::chrono::system_clock::now() < stop) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }
  return 0;
}
kreuzerkrieg
  • 3,009
  • 3
  • 28
  • 59