【问题标题】:Multiple threads queuing for global lock should all return true once first lock acquired一旦获得第一个锁,排队等待全局锁的多个线程都应该返回 true
【发布时间】:2011-03-29 14:48:22
【问题描述】:

类似的问题是这个:Are threads waiting on a lock FIFO? 但是,在这个问题中,一旦获得锁,只有一个线程执行受保护的代码,最终所有线程都会执行代码。

我想做的是执行一次受保护的代码,但是对于此时排队等待方法调用的所有线程,返回true。

基本上,受保护的代码是一个全局检查点,它与此时等待的所有线程相关。即,连续执行 N 个检查点不会超过 1 个。

请注意,当检查点完成时,会有其他对该方法的调用,它们本身需要一个新的检查点调用。

我相信我想做的是“批量”同步调用全局函数。

我如何在 C++ 中实现这一点,也许使用 Boost?

【问题讨论】:

    标签: c++ multithreading boost synchronization


    【解决方案1】:

    你似乎在寻找try_lock()

    给定一些 Boost.Thread Lockable,如果此时可以获取锁,则调用 Lockable::try_lock() 将返回 true,否则如果无法获取锁,则返回 false。

    当你的线程到达一个检查点时,让它尝试获取这个锁。如果失败,则另一个线程已经在函数中。如果成功,请检查一些bool 以查看检查点是否已运行。如果已运行,请释放锁并继续。如果还没有运行,保持锁,运行检查点函数,设置检查点bool为true。

    【讨论】:

    • 说 try_lock() 为某些线程返回 false。这意味着这个线程需要等待锁,而且不管bool是真还是假,我都不能确定checkpoint是针对这个线程的状态运行的吗?
    • 如果try_lock()为某个线程返回false,这意味着锁已经被另一个线程拥有。假设每个检查点都有一个锁,您可以安全地假设另一个线程当前正在执行检查点代码或已经执行了检查点代码。我误会你想要什么了吗?
    【解决方案2】:

    您似乎想要的看起来像是boost 提供的障碍。但是,如果这对您没有帮助,您可以使用条件变量制作一些东西,也在 boost

    【讨论】:

    • 非常有趣。如果对于所有 N 个线程,仅 1 次调用全局函数就足够了,并且给出:“wait() 返回:N 个线程中的一个将收到 true 的返回值,其他线程将收到 false 的值。”我应该能够将它与一个全局变量结合起来,该变量由获得 True 作为响应的线程设置,并且每个 False 线程都在等待?
    • 另外,这似乎假设一个固定的 N。如果 N 是未知的,甚至可能非常低,比如 1 或 2?
    • 经过一番思考,我想那个障碍不是你想要的。或者,dauphics 方法是可以的,只要锁定,您就返回,或者您需要一个额外的计时器来延迟和分组您的访问,并且您使用条件变量进行分组
    • 你的意思是屏障上的看门狗定时器->wait() --屏障填满还是定时器触发函数调用?
    • 不,我的意思是一个分组计时器来制作你的批处理,所以延迟之后,可能会有 5 个线程等待。然后你可以通知1执行,然后通知其他(广播)返回。您是否已经看过提升条件变量?
    【解决方案3】:

    这是我将如何做的伪代码。我假设存在具有 lock()unlock() 操作的 mutex 类。

    // This forward declaration helps with declaration
    // of the "friend" status for the nested class.
    class DoItOnce;
    
    class DoItOnce
    {
    private:
        bool    m_amFirst;
        mutex   m_mutex;
        friend class ::DoItOnce::Op;    
    public:
        DoItOnce()
        {
            m_amFirst = true;
            init(m_mutex);
        }
        ~DoItOnce() { destroy(m_mutex); }
        void reset()
        {
            m_mutex.lock();
            m_amFirst = true;
            m_mutex.lock();
        }
    
        //--------
        // Nested class
        //--------
        class Op {
        public:
            Op(DoItOnce & sync)
                : m_sync(sync)
            {
                m_sync.m_mutex.lock();
                m_amFirst = m_sync.m_amFirst;
                m_sync.m_amFirst = false;
            }
            ~Op() { m_sync.m_mutex.unlock(); }
            bool amFirst() { return m_amFirst; }
        private:
            DoItOnce &  m_sync;
            bool        m_amFirst;
        }; // end of nested class
    }; // end of outer class
    

    这里有一个例子来说明它的预期用途。您将实现doWork() 操作并让所有线程调用它。

    class WorkToBeDoneOnce
    {
    private:
        DoItOnce    m_sync;    
    public:
        bool doWork()
        {
            DoItOnce::Op    scopedLock(m_sync);
    
            if (!scopedLock.amFirst()) {
                // The work has already been done.
                return true;
            }
            ... // Do the work
            return true;
        }
        void resetAmFirstFlag()
        {
            m_sync.reset();
        }
    }
    

    如果您对我对 DoItOnce::Op 嵌套类的使用感到困惑,那么您可以在我的 通用同步策略 论文中找到这个编码习惯的解释,该论文在 here 中以各种形式提供格式(HTML、PDF 和幻灯片)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-07
      • 1970-01-01
      • 2019-07-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多