Событие сброса Win32 как класс синхронизации с boost C ++ - PullRequest
4 голосов
/ 14 января 2011

Мне нужен механизм, напоминающий события сброса Win32, которые я могу проверить с помощью функций, имеющих одинаковую семантику с WaitForSingleObject () и WaitForMultipleObjects () (на данный момент нужна только версия ..SingleObject ()). Но я ориентируюсь на несколько платформ, поэтому все, что у меня есть, это boost :: threads (AFAIK). Я придумал следующий класс и хотел спросить о потенциальных проблемах и о том, соответствует ли это заданию или нет. Заранее спасибо.

class reset_event
{
 bool flag, auto_reset;
 boost::condition_variable cond_var;
 boost::mutex mx_flag;

public:
 reset_event(bool _auto_reset = false) : flag(false), auto_reset(_auto_reset)
 {
 }

 void wait()
 {
  boost::unique_lock<boost::mutex> LOCK(mx_flag);
  if (flag)
   return;

  cond_var.wait(LOCK);
  if (auto_reset)
   flag = false;
 }

 bool wait(const boost::posix_time::time_duration& dur)
 {
  boost::unique_lock<boost::mutex> LOCK(mx_flag);
  bool ret = cond_var.timed_wait(LOCK, dur) || flag;
  if (auto_reset && ret)
   flag = false;

  return ret;
 }

 void set()
 {
  boost::lock_guard<boost::mutex> LOCK(mx_flag);
  flag = true;
  cond_var.notify_all();
 }

 void reset()
 {
  boost::lock_guard<boost::mutex> LOCK(mx_flag);
  flag = false;
 }
};

Пример использования;

reset_event terminate_thread;

void fn_thread()
{
 while(!terminate_thread.wait(boost::posix_time::milliseconds(10)))
 {
  std::cout << "working..." << std::endl;
  boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
 }

 std::cout << "thread terminated" << std::endl;
}

int main()
{
 boost::thread worker(fn_thread);

 boost::this_thread::sleep(boost::posix_time::seconds(1));
 terminate_thread.set();

 worker.join();

 return 0;
}

EDIT

Я исправил код в соответствии с предложениями Майкла Берра. Мои "очень простые" тесты не показывают проблем.

class reset_event
{
    bool flag, auto_reset;
    boost::condition_variable cond_var;
    boost::mutex mx_flag;

public:
    explicit reset_event(bool _auto_reset = false) : flag(false), auto_reset(_auto_reset)
    {
    }

    void wait()
    {
        boost::unique_lock<boost::mutex> LOCK(mx_flag);
        if (flag)
        {
            if (auto_reset)
                flag = false;
            return;
        }

        do
        {
            cond_var.wait(LOCK);
        } while(!flag);

        if (auto_reset)
            flag = false;
    }

    bool wait(const boost::posix_time::time_duration& dur)
    {
        boost::unique_lock<boost::mutex> LOCK(mx_flag);
        if (flag)
        {
            if (auto_reset)
                flag = false;
            return true;
        }

        bool ret = cond_var.timed_wait(LOCK, dur);
        if (ret && flag)
        {
            if (auto_reset)
                flag = false;

            return true;
        }

        return false;
    }

    void set()
    {
        boost::lock_guard<boost::mutex> LOCK(mx_flag);
        flag = true;
        cond_var.notify_all();
    }

    void reset()
    {
        boost::lock_guard<boost::mutex> LOCK(mx_flag);
        flag = false;
    }
};

Ответы [ 2 ]

3 голосов
/ 14 января 2011

Несколько вещей, которые вы захотите проверить / исправить (обратите внимание - я ни в коем случае не говорю, что это единственные вещи - у меня был только быстрый взгляд):

  • в вашей функции wait() вы не сбрасываете уже сигнализированное событие, если оно настроено на auto_reset:

     void wait()
     {
      boost::unique_lock<boost::mutex> LOCK(mx_flag);
      if (flag) {
       if (auto_reset) flag = false;    // <-- I think you need this
       return;
      }
    
      cond_var.wait(LOCK);
      if (auto_reset)
       flag = false;
     }
    
  • в wait(const boost::posix_time::time_duration& dur) васследует проверить flag до ожидания в условной переменной.

  • в обеих wait функциях, если вы ждете переменную условия, вам может потребоваться перепроверить флаг, чтобы убедиться, что какой-то другой поток не сбросил событие в это время,Это особенно верно для событий auto_reset, которые должны высвобождать только одного официанта, даже когда несколько событий ожидают события.

1 голос
/ 30 августа 2012

Вот моя версия, которая была слегка подправлена ​​для достижения следующего:

  • Не блокировать производителя с помощью set (), reset () и т. Д. Вместо этого он считает количество «выпусков», а непотеря отображения 1: 1 после того, как логическое условие истинно.
  • Позволяет внешним вызывающим сторонам указывать мьютекс для wait (), в любом случае это часто является внешним ресурсом в моих сценариях использования и может быть отделен от внутреннего мьютекса.
  • Перемещенные вызовы set (), reset_one (), reset_all () для внутреннего мьютекса, теперь они не блокируются при повторном вызове до вызова потребителя wait ().

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

Прогресс в моем проекте Использование ....

Переменная условия повышения: -> отправка 3 запросов на загрузку, поток занят и видит только 1 или 2.
Отправленный ответ с использованием Bool: -> отправить 3 запроса на загрузку, блоки производителя на 2-й запрос из-за общего мьютекса.производитель не разблокирует, пока не обработан 1-й запрос на загрузку.
Моя версия -> отправляет 3 запроса на загрузку, производитель немедленно возвращает все 3, потребитель видит 3 запроса на загрузку медленно, но верно:)

Надеюсь, это кому-нибудь поможет.


    class cNonLossyCondition
    {
        bool flag, auto_reset;
        boost::condition_variable cond_var;
        int lost_signals;
        boost::mutex internal_mutex;

    public:
        cNonLossyCondition(bool _auto_reset)
        {
            this->flag = false;
            this->auto_reset = auto_reset;
            this->lost_signals = 0;
        }

        void wait(boost::mutex* mx_flag)
        {
            boost::unique_lock LOCK(*mx_flag);
            if (flag)
            {
                if (auto_reset)
                    this->reset_one();
                return;
            }

            do
            {
                cond_var.wait(LOCK);
            } while(!flag);

            if (auto_reset)
                this->reset_one();
        }

        bool wait(boost::mutex* mx_flag,const boost::posix_time::time_duration& dur)
        {
            boost::unique_lock LOCK(*mx_flag);
            if (flag)
            {
                if (auto_reset)
                    this->reset_one();
                return true;
            }

            bool ret = cond_var.timed_wait(LOCK, dur);
            if (ret && flag)
            {
                if (auto_reset)
                    this->reset_one();

                return true;
            }

            return false;
        }

        void set()
        {
            boost::lock_guard LOCK(this->internal_mutex);
            flag = true;
            if (this->lost_signals lost_signals = 1; //already incremented
            } else {
                this->lost_signals = this->lost_signals + 1;
            }

            cond_var.notify_all();
        }

        void reset_one()
        {
            boost::lock_guard LOCK(this->internal_mutex);
            this->lost_signals = this->lost_signals - 1;
            if (this->lost_signals lost_signals = 0;
                flag = false;
            }

        }
        void reset_all()
        {
            boost::lock_guard LOCK(this->internal_mutex);
            flag = false;
            this->lost_signals = 0;
        }
    };

...