Подтвердить что ты не робот

Как создать события таймера с помощью С++ 11?

Как создать события таймера с помощью С++ 11?

Мне нужно что-то вроде: "Позвони мне через 1 секунду".

Есть ли библиотека?

4b9b3361

Ответ 1

Сделал простую реализацию того, что я считаю тем, чего вы хотите достичь. Вы можете использовать класс later со следующими аргументами:

  • int (миллисекунды, чтобы дождаться запуска кода)
  • bool (если true, он мгновенно возвращается и запускает код после указанного времени в другом потоке)
  • переменные аргументы (именно то, что вы пишете std:: bind)

Вы можете изменить std::chrono::milliseconds на std::chrono::nanoseconds или microseconds для еще большей точности и добавить второй цикл int и for, чтобы указать, сколько раз запускать код.

Здесь вы идете, наслаждайтесь:

#include <functional>
#include <chrono>
#include <future>
#include <cstdio>

class later
{
public:
    template <class callable, class... arguments>
    later(int after, bool async, callable&& f, arguments&&... args)
    {
        std::function<typename std::result_of<callable(arguments...)>::type()> task(std::bind(std::forward<callable>(f), std::forward<arguments>(args)...));

        if (async)
        {
            std::thread([after, task]() {
                std::this_thread::sleep_for(std::chrono::milliseconds(after));
                task();
            }).detach();
        }
        else
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(after));
            task();
        }
    }

};

void test1(void)
{
    return;
}

void test2(int a)
{
    printf("%i\n", a);
    return;
}

int main()
{
    later later_test1(1000, false, &test1);
    later later_test2(1000, false, &test2, 101);

    return 0;
}

Вывод через две секунды:

101

Ответ 2

Если вы находитесь в Windows, вы можете использовать класс concurrency:: timer для планирования обратного вызова без необходимости беспокоиться о управлении потоками и без блокировки текущей нити.

#include <iostream>
#include <agents.h>
#include <ppltasks.h>

template <class T>
void call_after(const T& callback, unsigned int timeInMs)
{
    concurrency::task_completion_event<void> tce;
    auto call = new concurrency::call<int>(
        [callback, tce](int)
        {
            callback();
            tce.set();
        });

    auto timer = new concurrency::timer<int>(timeInMs, 0, call, false);
    concurrency::task<void> event_set(tce);
    event_set.then([timer, call]()
    {
        delete call;
        delete timer;
    });

    timer->start();
}

int main()
{
    std::function<void()> callback = []{ std::cout << "in callback\n"; };
    call_after(callback, 1000);
    std::cin.get();
}

Также проверьте эту статью.

Если вы являетесь чистым энтузиастом окон или не предпочитаете использовать ppl, вы можете использовать CreateWaitableTimer и RegisterWaitForSingleObject, чтобы запланировать обратный вызов по умолчанию в потоке по умолчанию.

#include <Windows.h>
#include <string>
#include <iostream>
#include <functional>
#include <memory>

template <class T>
class Timer
{
public:
    Timer(const T& callback, long long timeInHundredNanos)
    {
        HANDLE timer = CreateWaitableTimer(nullptr, true, nullptr);
        if (timer == nullptr)
        {
            throw std::exception("Could not create waitable timer");
        }

        auto state = std::make_unique<State>(timer, nullptr, callback);

        LARGE_INTEGER dueTime;
        dueTime.QuadPart = -timeInHundredNanos;
        if (!SetWaitableTimer(timer, &dueTime, 0, nullptr, nullptr, false))
        {
            throw std::exception("Could not set waitable timer");
        }

        HANDLE waitHandle;
        if (!RegisterWaitForSingleObject(
                &waitHandle,
                timer,
                [](void* state, unsigned char)
                {
                    std::unique_ptr<State> callbackState((State*)state);
                    callbackState->m_callback();
                },
                state.get(),
                INFINITE,
                WT_EXECUTEONLYONCE))
        {
            throw std::exception("Could not register wait");
        };

        state->m_waitHandle = waitHandle;
        state.release();
    }

private:
    class State
    {
    public:
        State(HANDLE timer, HANDLE waitHandle, const T& callback)
            :m_timer(timer), m_waitHandle(waitHandle), m_callback(callback)
        {
        }

        ~State()
        {
            if (m_waitHandle != nullptr)
            {
                UnregisterWait(m_waitHandle);
            }

            if (m_timer != nullptr)
            {
                CloseHandle(m_timer);
            }
        }

        HANDLE m_waitHandle;
        HANDLE m_timer;
        T m_callback;
    };
};

template <class T>
void call_after(const T& callback, unsigned int timeInMs)
{
    Timer<T> timer(callback, timeInMs * 10000);
}

int main(int argc, char* argv[])
{
    std::function<void()> callback = []{ std::cout << "in callback\n"; };
    call_after(callback, 1000);
    std::cin.get();
}

Ответ 3

Это код, который у меня есть до сих пор:

Я использую VС++ 2012 (без вариационных шаблонов)

//header
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <chrono>
#include <memory>
#include <algorithm>

template<class T>
class TimerThread
{
  typedef std::chrono::high_resolution_clock clock_t;

  struct TimerInfo
  {
    clock_t::time_point m_TimePoint;
    T m_User;

    template <class TArg1>
    TimerInfo(clock_t::time_point tp, TArg1 && arg1)
      : m_TimePoint(tp)
      , m_User(std::forward<TArg1>(arg1))
    {
    }

    template <class TArg1, class TArg2>
    TimerInfo(clock_t::time_point tp, TArg1 && arg1, TArg2 && arg2)
      : m_TimePoint(tp)
      , m_User(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2))
    {
    }
  };

  std::unique_ptr<std::thread> m_Thread;
  std::vector<TimerInfo>       m_Timers;
  std::mutex                   m_Mutex;
  std::condition_variable      m_Condition;
  bool                         m_Sort;
  bool                         m_Stop;

  void TimerLoop()
  {
    for (;;)
    {
      std::unique_lock<std::mutex>  lock(m_Mutex);

      while (!m_Stop && m_Timers.empty())
      {
        m_Condition.wait(lock);
      }

      if (m_Stop)
      {
        return;
      }

      if (m_Sort)
      {
        //Sort could be done at insert
        //but probabily this thread has time to do
        std::sort(m_Timers.begin(),
                  m_Timers.end(),
                  [](const TimerInfo & ti1, const TimerInfo & ti2)
        {
          return ti1.m_TimePoint > ti2.m_TimePoint;
        });
        m_Sort = false;
      }

      auto now = clock_t::now();
      auto expire = m_Timers.back().m_TimePoint;

      if (expire > now) //can I take a nap?
      {
        auto napTime = expire - now;
        m_Condition.wait_for(lock, napTime);

        //check again
        auto expire = m_Timers.back().m_TimePoint;
        auto now = clock_t::now();

        if (expire <= now)
        {
          TimerCall(m_Timers.back().m_User);
          m_Timers.pop_back();
        }
      }
      else
      {
        TimerCall(m_Timers.back().m_User);
        m_Timers.pop_back();
      }
    }
  }

  template<class T, class TArg1>
  friend void CreateTimer(TimerThread<T>& timerThread, int ms, TArg1 && arg1);

  template<class T, class TArg1, class TArg2>
  friend void CreateTimer(TimerThread<T>& timerThread, int ms, TArg1 && arg1, TArg2 && arg2);

public:
  TimerThread() : m_Stop(false), m_Sort(false)
  {
    m_Thread.reset(new std::thread(std::bind(&TimerThread::TimerLoop, this)));
  }

  ~TimerThread()
  {
    m_Stop = true;
    m_Condition.notify_all();
    m_Thread->join();
  }
};

template<class T, class TArg1>
void CreateTimer(TimerThread<T>& timerThread, int ms, TArg1 && arg1)
{
  {
    std::unique_lock<std::mutex> lock(timerThread.m_Mutex);
    timerThread.m_Timers.emplace_back(TimerThread<T>::TimerInfo(TimerThread<T>::clock_t::now() + std::chrono::milliseconds(ms),
                                      std::forward<TArg1>(arg1)));
    timerThread.m_Sort = true;
  }
  // wake up
  timerThread.m_Condition.notify_one();
}

template<class T, class TArg1, class TArg2>
void CreateTimer(TimerThread<T>& timerThread, int ms, TArg1 && arg1, TArg2 && arg2)
{
  {
    std::unique_lock<std::mutex> lock(timerThread.m_Mutex);
    timerThread.m_Timers.emplace_back(TimerThread<T>::TimerInfo(TimerThread<T>::clock_t::now() + std::chrono::milliseconds(ms),
                                      std::forward<TArg1>(arg1),
                                      std::forward<TArg2>(arg2)));
    timerThread.m_Sort = true;
  }
  // wake up
  timerThread.m_Condition.notify_one();
}

//sample
#include <iostream>
#include <string>

void TimerCall(int i)
{
  std::cout << i << std::endl;
}

int main()
{
  std::cout << "start" << std::endl;
  TimerThread<int> timers;

  CreateTimer(timers, 2000, 1);
  CreateTimer(timers, 5000, 2);
  CreateTimer(timers, 100, 3);

  std::this_thread::sleep_for(std::chrono::seconds(5));
  std::cout << "end" << std::endl;
}

Ответ 4

Асинхронное решение от Edward:

  • создать новый поток
  • спать в этом потоке
  • выполнить задачу в этом потоке

прост и может просто работать для вас.

Я также хотел бы предложить более продвинутую версию, которая имеет следующие преимущества:

  • нет накладных расходов на запуск
  • только один дополнительный поток на каждый процесс, необходимый для обработки всех заданных по времени задач.

Это может быть особенно полезно в крупных проектах программного обеспечения, где у вас много задач, выполняемых повторно в вашем процессе, и вы заботитесь об использовании ресурсов (потоках), а также о служебных нагрузках на запуск.

Идея: иметь один поток услуг, который обрабатывает все зарегистрированные заданные сроки. Используйте для этого boost io_service.

Код похож на: http://www.boost.org/doc/libs/1_65_1/doc/html/boost_asio/tutorial/tuttimer2/src.html

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

int main()
{
  boost::asio::io_service io;

  boost::asio::deadline_timer t(io, boost::posix_time::seconds(1));
  t.async_wait([](const boost::system::error_code& /*e*/){
    printf("Printed after 1s\n"); });

  boost::asio::deadline_timer t2(io, boost::posix_time::seconds(1));
  t2.async_wait([](const boost::system::error_code& /*e*/){
    printf("Printed after 1s\n"); });

  // both prints happen at the same time,
  // but only a single thread is used to handle both timed tasks
  // - namely the main thread calling io.run();

  io.run();

  return 0;
}