【问题标题】:waiting for multiple condition variables in boost?等待提升中的多个条件变量?
【发布时间】:2023-03-04 20:10:01
【问题描述】:

我正在寻找一种方法来等待多个条件变量。 IE。类似:

boost::condition_variable cond1;  
boost::condition_variable cond2;

void wait_for_data_to_process()  
{  
    boost::unique_lock<boost::mutex> lock(mut);

    wait_any(lock, cond1, cond2); //boost only provides cond1.wait(lock);

    process_data();
}

条件变量是否可能发生这种情况。如果没有,还有其他解决方案吗?

谢谢

【问题讨论】:

    标签: c++ boost multithreading


    【解决方案1】:

    我不相信你可以用 boost::thread 做这样的事情。也许是因为 POSIX 条件变量不允许这种类型的构造。当然,Windows 有 WaitForMultipleObjects as aJ 发布,如果您愿意将代码限制为 Windows 同步原语,这可能是一个解决方案。

    另一种选择是使用更少的条件变量:只要有 1 个条件变量,当任何“有趣”的事情发生时,你就会触发它。然后,任何时候你想等待,你运行一个循环来检查你感兴趣的特定情况是否已经出现,如果没有,则返回等待条件变量。无论如何,您应该在这样的循环中等待这些条件变量,因为条件变量等待会受到虚假唤醒(来自 boost::thread 文档,强调我的):

    void wait(boost::unique_lock&lt;boost::mutex&gt;&amp; lock)
    ...
    效果:
    原子地调用lock.unlock() 并阻塞当前线程。当调用this-&gt;notify_one()this-&gt;notify_all()虚假地 通知时,线程将解除阻塞。 ...

    【讨论】:

    • 在我看来,为“更改 1 或 2”设置第三个条件变量确实是最好的方法。另一种方法是使用 select 将等待条件转换为等待文件描述符,并使用管道进行线程通信。由于这个问题是笼统的(它没有解释等待什么),因此尚不清楚 select 会更好。
    • 使用一个条件变量的问题是封装。你不能拥有像GoStyleChannel::receive_but_break_if(condition_variable&amp; cv, function&amp; pred) 这样的函数。换句话说,假设您有一些已经使用条件变量的阻塞函数。您希望它阻塞直到条件发生您的条件发生。如果没有全局std::condition_variable somethingHasHappenedSomewhere,你就无法做到这一点,这真的很垃圾。
    【解决方案2】:

    正如 Managu 已经回答的那样,您可以使用相同的条件变量并在您的 while 循环中检查多个“事件”(布尔变量)。但是,必须使用 condvar 使用的同一个互斥锁来保护对这些布尔变量的并发访问。

    由于我已经为相关的question 输入了此代码示例,因此我将在此处重新发布:

    boost::condition_variable condvar;
    boost::mutex mutex;
    bool finished1 = false;
    bool finished2 = false;
    
    void longComputation1()
    {
        {
            boost::lock_guard<boost::mutex> lock(mutex);
            finished1 = false;
        }
        // Perform long computation
        {
            boost::lock_guard<boost::mutex> lock(mutex);
            finished1 = true;
        }
        condvar.notify_one();
    }
    
    void longComputation2()
    {
        {
            boost::lock_guard<boost::mutex> lock(mutex);
            finished2 = false;
        }
        // Perform long computation
        {
            boost::lock_guard<boost::mutex> lock(mutex);
            finished2 = true;
        }
        condvar.notify_one();
    }
    
    void somefunction()
    {
        // Wait for long computations to finish without "spinning"
        boost::lock_guard<boost::mutex> lock(mutex);
        while(!finished1 && !finished2)
        {
            condvar.wait(lock);
        }
    
        // Computations are finished
    }
    

    【讨论】:

      【解决方案3】:
      alternative solutions?
      

      我不确定 Boost 库,但您可以使用 WaitForMultipleObjects 函数来等待多个内核对象。看看这是否有帮助。

      【讨论】:

      【解决方案4】:

      正如 Managu 指出的那样,首先使用多个条件可能不是一个好的解决方案。你想做的事情应该可以使用信号量来实现。

      【讨论】:

        【解决方案5】:

        对多个事件使用相同的条件变量在技术上是可行的,但它不允许封装。所以我尝试制作一个支持它的类。 尚未测试!它也不支持notify_one(),因为我还没有弄清楚如何实现它。

        #pragma once
        
        #include <condition_variable>
        #include <unordered_set>
        
        // This is like a `condition_variable` but you can wait on multiple `multi_condition_variable`s.
        // Internally it works by creating a new `condition_variable` for each `wait_any()` and registering
        // it with the target `multi_condition_variable`s. When `notify_all()` is called, the main `condition_variable`
        // is notified, as well as all the temporary `condition_variable`s created by `wait_any()`.
        //
        // There are two caveats:
        //
        //  1. You can't call the destructor if any threads are `wait()`ing. This is difficult to get around but
        //     it is the same as `std::wait_condition` anyway.
        //
        //  2. There is no `notify_one()`. You can *almost* implement this, but the only way I could think to do
        //     it was to add an `atomic_int` that indicates the number of waits(). Unfortunately there is no way
        //     to atomically increment it, and then wait.
        class multi_condition_variable
        {
        public:
            multi_condition_variable()
            {
            }
        
            // Note that it is only safe to invoke the destructor if no thread is waiting on this condition variable.
            ~multi_condition_variable()
            {
            }
        
            // Notify all threads calling wait(), and all wait_any()'s that contain this instance.
            void notify_all()
            {
                _condition.notify_all();
                for (auto o : _others)
                    o->notify_all();
            }
        
            // Wait for notify_all to be called, or a spurious wake-up.
            void wait(std::unique_lock<std::mutex>& loc)
            {
                _condition.wait(loc);
            }
        
            // Wait for any of the notify_all()'s in `cvs` to be called, or a spurious wakeup.
            static void wait_any(std::unique_lock<std::mutex>& loc, std::vector<std::reference_wrapper<multi_condition_variable>> cvs)
            {
                std::condition_variable c;
                for (multi_condition_variable& cv : cvs)
                    cv.addOther(&c);
                c.wait(loc);
                for (multi_condition_variable& cv : cvs)
                    cv.removeOther(&c);
            }
        
        private:
            void addOther(std::condition_variable* cv)
            {
                std::lock_guard<std::mutex> lock(_othersMutex);
                _others.insert(cv);
            }
        
            void removeOther(std::condition_variable* cv)
            {
                // Note that *this may have been destroyed at this point.
                std::lock_guard<std::mutex> lock(_othersMutex);
                _others.erase(cv);
            }
        
            // The condition variable.
            std::condition_variable _condition;
        
            // When notified, also notify these.
            std::unordered_set<std::condition_variable*> _others;
        
            // Mutex to protect access to _others.
            std::mutex _othersMutex;
        };
        
        // Example use:
        //
        //  multi_condition_variable cond1;
        //  multi_condition_variable cond2;
        //
        //  void wait_for_data_to_process()
        //  {
        //      unique_lock<boost::mutex> lock(mut);
        //
        //      multi_condition_variable::wait_any(lock, {cond1, cond2});
        //
        //      process_data();
        //  }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-07-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-20
          • 1970-01-01
          相关资源
          最近更新 更多