Win32重置事件,如使用boost C ++的同步类

时间:2011-01-14 15:32:24

标签: c++ multithreading synchronization cross-platform autoresetevent

我需要一些让人想起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;
}

修改

我根据Michael Burr的建议修改了代码。我的“非常简单”的测试表明没有问题。

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 个答案:

答案 0 :(得分:3)

你要检查/修复的一些事情(注意 - 我绝不是说这些是唯一的东西 - 我只是快速浏览一下):

  • 在您的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 :(得分:1)

这是我的版本,稍作调整以实现以下目的。

  • 使用set(),reset()等阻止生产者,而不是在布尔条件为真后计算“版本”的数量,而不是丢失1:1映射。
  • 允许外部呼叫者为wait()指定互斥锁,在我的使用场景中它通常是外部资源,并且可以与内部互斥锁分开。
  • 将set(),reset_one(),reset_all()调用移动到内部互斥锁,现在在消费者调用wait()之前重复调用它们时不会阻塞。

现在我的加载线程可以排队多个长期存在的请求,而不会在繁忙处理时丢弃任何任务。

我的项目中的进展使用....

提升条件变量: - &gt;发送3个加载请求,线程忙,只看到1或2 使用Bool发布答案: - &gt;由于共享的互斥锁,在第二个请求上发送3个加载请求,生成器块。生产者在第一次加载请求处理之前不会解除阻止 我的版本 - &gt;发送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;
        }
    };