Улучшение реализации таймера (timeout event) в C ++ - PullRequest
0 голосов
/ 14 февраля 2019

Мой случай выглядит следующим образом: программа использует два потока, назовем их «Отправитель» и «Получатель», потому что это механизм межпроцессного взаимодействия.

Поток «Отправитель» после отправки сообщения останавливаетсяпри условии, предоставленном std::condition_variable и функцией .wait (Lock).Поток «Получатель» информирует ожидающий поток об ответе на его сообщение, используя .notify_one().

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

Я подготовил следующий класс (я хотел бы, чтобы он был универсальным, чтобы функция уведомления определялась из внешнего класса), но я уверен, что он может быть реализован лучше.Я хотел избежать большой загрузки процессора, поэтому я использовал std::this_thread::sleep_for, но я полагаю, что его можно как-то заменить на std::this_thread::yield().Я хотел бы использовать, например, std::future_status, но я не знаю, как это сделать.Как это можно улучшить?Я могу использовать стандартный c++11 или boost 1.55.

class Timer
{
    private:
    int MsLimit;
    std::atomic<bool> Stop;
    std::atomic<bool> LimitReached;
    std::thread T;
    std::mutex M;
    std::function<void()> NotifyWaitingThreadFunction;

    void Timeout()
    {
        std::unique_lock<std::mutex> Lock(M);
        std::chrono::system_clock::time_point TimerStart = std::chrono::system_clock::now();
        std::chrono::duration<long long, std::milli> ElapsedTime;
        unsigned int T = 0;
        do
        {   std::this_thread::sleep_for(std::chrono::milliseconds(5));
            std::chrono::system_clock::time_point TimerEnd = std::chrono::system_clock::now();
            ElapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>(TimerEnd - TimerStart);
            T+=ElapsedTime.count();
            if((T > MsLimit) && (!Stop))
            {   LimitReached = true;
                Stop = true;
            }
        }while(!Stop);

        if(LimitReached)
        {
            NotifyWaitingThreadFunction();
        }
    }

    public:
    Timer(int Milliseconds) : MsLimit(Milliseconds)
    {

    }
    void StartTimer()
    {
        Stop = false;
        LimitReached = false;
        T = std::thread(&Timer::Timeout,this);
    }
    void StopTimer()
    {
        std::unique_lock<std::mutex> Lock(M);
        Stop = true;
        LimitReached = false;
    }
    template<class T>
    void AssignFunction(T* ObjectInstance, void (T::*MemberFunction)())
    {
        NotifyWaitingThreadFunction = std::bind(MemberFunction,ObjectInstance);
    }
};

1 Ответ

0 голосов
/ 14 февраля 2019

В вашем решении есть одна ошибка - do, пока цикл выполняется до тех пор, пока не истечет MsLimit.После запуска Timeout мьютекс M блокируется, и вызов StopTimer не может разорвать цикл, поскольку stop в StopTimer устанавливается в значение true, когда M освобождается в Timeout, что происходит, если (T > MsLimit) возвращает true и функция завершается.Кстати, использование mutex является избыточным, поскольку Stop является атомарным.

Вы можете использовать один из таймеров из библиотеки повышения вместо создания собственной.

В приведенном ниже коде используетсяboost::asio::high_resolution_timer (в версии Boost 1.55):

class Timer 
{
public:
  Timer (int ms)
  : timer(io), ms(ms) {}

  ~Timer() {if(t.joinable()) t.join();}

  void Start() {
    t = std::thread( [this]()
    {
      timer.expires_from_now(std::chrono::milliseconds(ms));
      timer.async_wait([this](const boost::system::error_code& ec) // start async wait
        { // lambda is called when timeout expired or error occures
          if (!ec) // if there is no error, call function
            NotifyWaitingThreadFunction();
        });
      io.run(); // process async operations
    });
  }

  void Stop() {
    timer.cancel();
  }

  template<class T>
  void AssignFunction(T* ObjectInstance, void (T::*MemberFunction)())
  {
      NotifyWaitingThreadFunction = std::bind(MemberFunction,ObjectInstance);
  }
private:
  boost::asio::io_service io; // needed for timer
  boost::asio::high_resolution_timer timer;
  std::thread t;
  int ms;
  std::function<void()> NotifyWaitingThreadFunction;
};

В Start создается поток метода, в котором мы устанавливаем значение тайм-аута в мс, таймер запускается async_wait.Лямбда, переданная в async_wait, вызывается, когда истекло время ожидания или произошла ошибка.Поэтому, если ошибки нет, вы можете позвонить NotifyWaitingThreadFunction.Для остановки таймера используйте метод Stop.Стоп отменяет запущенную асинхронную операцию, затем вызывается лямбда с ec == boost::asio::error::operation_aborted.В этом случае лямбда заканчивается без вызова NotifyWaitingThreadFunction.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...