【问题标题】:Select mutex or dummy mutex at runtime在运行时选择互斥锁或虚拟互斥锁
【发布时间】:2011-01-12 00:02:17
【问题描述】:

我有一个在多个项目之间共享的类,它的一些用途是单线程的,有些是多线程的。单线程用户不希望互斥锁的开销,而多线程用户不希望自己进行锁定,希望能够有选择地以“单线程模式”运行。所以我希望能够在运行时在真实和“虚拟”互斥锁之间进行选择。

理想情况下,我会拥有一个shared_ptr<something> 并分配一个真实或虚假的互斥对象。然后我会“锁定”它而不考虑里面有什么。

unique_lock<something> guard(*mutex);
... critical section ...

现在有一个signals2::dummy_mutex,但它不与boost::mutex 共享一个公共基类。

那么,在不使锁定/保护代码比上面的示例更复杂的情况下,在真正的互斥锁和虚拟互斥锁(信号 2 中的一个或其他)之间进行选择的优雅方法是什么?

而且,在您指出替代方案之前:

  • 我可以在编译时选择一个实现,但是预处理器宏很难看,维护项目配置对我们来说很痛苦。
  • 多线程环境中的类用户不想承担锁定类使用的责任,而不是让类在内部执行自己的锁定。
  • “线程安全包装器”涉及的 API 和现有用法太多,无法成为实用的解决方案。

【问题讨论】:

    标签: c++ multithreading mutex boost-thread


    【解决方案1】:

    这还不够吗?

       class SomeClass
        {
        public:
            SomeClass(void);
            ~SomeClass(void);
            void Work(bool isMultiThreaded = false)
            {
                if(isMultiThreaded)
               {
                   lock // mutex lock ...
                   {
                        DoSomething
                   }
               }
               else
               {
                    DoSomething();
               }
           }   
        };
    

    【讨论】:

    • 我已经有很多地方使用了我描述的 RAII 锁定模式。将它们全部更改为使用这种冗长的包装器是不切实际的。
    【解决方案2】:

    这样的事情怎么样? 它未经测试,但应该接近确定。 您可能会考虑让模板类保存一个值而不是一个指针 如果您的互斥锁支持正确的结构。否则,您可以专门化 MyMutex 类以获取价值行为。

    此外,复制或销毁也不小心。我将其作为练习留给读者;)(shared_ptr 或存储值而不是指针应该可以解决此问题)

    哦,使用 RAII 而不是显式锁定/解锁的代码会更好......但这是一个不同的问题。我假设这就是你代码中的 unique_lock 的作用?

    struct IMutex
    {
      virtual ~IMutex(){}
      virtual void lock()=0;
      virtual bool try_lock()=0;
      virtual void unlock()=0;
    };
    
    template<typename T>
    class MyMutex : public IMutex
    {
      public:
        MyMutex(T t) : t_(t) {}
        void lock() { t_->lock(); }
        bool try_lock() { return t_->try_lock(); }
        void unlock() { t_->unlock(); }
      protected:
        T* t_;
    };
    
    IMutex * createMutex()
    {
      if( isMultithreaded() )
      {
         return new MyMutex<boost::mutex>( new boost::mutex );
      }
      else
      {
         return new MyMutex<signal2::dummy_mutex>( new signal2::dummy_mutex );
      }
    }
    
    
    int main()
    {
       IMutex * mutex = createMutex();
       ...
       {
         unique_lock<IMutex> guard( *mutex );
         ...
       }
    
    }
    

    【讨论】:

    【解决方案3】:

    由于signals2::dummy_mutexboost::mutex 这两个互斥类不共享一个公共基类,您可以使用类似“external polymorphism”的内容来允许对它们进行多态处理。然后,您可以将它们用作锁定 strategies 以获得通用互斥锁/锁定接口。这使您可以避免在锁实现中使用“if”语句。

    注意:这基本上是 Michael 提出的解决方案所实现的。我建议他的回答。

    【讨论】:

      【解决方案4】:

      一般来说,只有当资源在多个进程之间共享时才需要互斥锁。如果对象的实例对于(可能是多线程的)进程来说是唯一的,那么关键部分通常更合适。

      在 Windows 中,关键部分的单线程实现一个虚拟的。不确定您使用的是什么平台。

      【讨论】:

        【解决方案5】:

        你听说过Policy-based Design吗?

        您可以定义一个Lock Policy 接口,用户可以选择她想要的策略。为了便于使用,“默认”策略使用编译时变量进行精确化。

        #ifndef PROJECT_DEFAULT_LOCK_POLICY
        #define PROJECT_DEFAULT_LOCK_POLICY TrueLock
        #endif
        
        template <class LP = PROJECT_DEFAULT_LOCK_POLICY>
        class MyClass {};
        

        这样,您的用户可以通过简单的编译时开关来选择他们的策略,并且可以一次覆盖一个实例;)

        【讨论】:

        • 那肯定行得通,这就是现有的 boost "dummy_mutex" 类在信号2中的使用方式。不幸的是,我不想将类变成模板并将其所有代码移动到头文件中,我试图最小化现有代码的更改范围。
        • 另外,这在编译时有效,而 OP 要求运行时解决方案
        【解决方案6】:

        仅供参考,这是我最终完成的实现。

        我取消了抽象基类,将它与无操作“虚拟”实现合并。还要注意带有隐式转换运算符的shared_ptr 派生类。我认为这有点太棘手了,但它让我可以使用 shared_ptr&lt;IMutex&gt; 对象,而我以前使用 boost::mutex 对象,变化为零。

        头文件:

        class Foo {
           ...
        private:
            struct IMutex {
                virtual ~IMutex()       { }
                virtual void lock()     { }
                virtual bool try_lock() { return true; }
                virtual void unlock()   { }
            };
            template <typename T> struct MutexProxy;
        
            struct MutexPtr : public boost::shared_ptr<IMutex> {
                operator IMutex&() { return **this; }
            };
        
            typedef boost::unique_lock<IMutex> MutexGuard;
        
            mutable MutexPtr mutex;
        };
        

        实现文件:

        template <typename T>
        struct Foo::MutexProxy : public IMutex {
            virtual void lock()     { mutex.lock(); }
            virtual bool try_lock() { return mutex.try_lock(); }
            virtual void unlock()   { mutex.unlock(); }
        private:
            T mutex;
        };
        
        Foo::Foo(...) {
            mutex.reset(single_thread ? new IMutex : new MutexProxy<boost::mutex>);
        }
        
        Foo::Method() {
            MutexGuard guard(mutex);
        }
        

        【讨论】:

          【解决方案7】:

          这是我的解决方案:

          std::unique_lock<std::mutex> lock = dummy ?
               std::unique_lock<std::mutex>(mutex, std::defer_lock) :
               std::unique_lock<std::mutex>(mutex);
          

          【讨论】:

            【解决方案8】:

            基于策略的选项:

            class SingleThreadedPolicy {
            public:
                class Mutex {
                public:
                    void Lock() {}
                    void Unlock() {}
                    bool TryLock() { return true; }
                };
            
                class ScopedGuard {
                public:
                    ScopedGuard(Mutex& mutex) {}
                };
            };
            
            class MultithreadingPolicy {
            public:
            class ScopedGuard;
            
            class Mutex {
            friend class ScopedGuard;
            
            private:
                std::mutex  mutex_;
            
            public:
                void Lock() {
                    mutex_.lock();
                }
                void Unlock() {
                    mutex_.unlock();
                }
                bool TryLock() {
                    return mutex_.try_lock();
                }
            };
            
            class ScopedGuard {
            private:
                std::lock_guard<std::mutex> lock_;
                
            public:
                ScopedGuard(Mutex& mutex) : lock_(mutex.mutex_) {}
            };
            };
            

            那么可以这样使用:

            template<class ThreadingPolicy = SingleThreadedPolicy>
            class MyClass {
            private:
                typedef typename ThreadingPolicy::Mutex Mutex;
                typedef typename ThreadingPolicy::ScopedGuard ScopedGuard;
                
                Mutex mutex_;
            
            public:
                void DoSomething(){
                    ScopedGuard guard(mutex_);
                    std::cout<<"Hello World"<<std::endl;
                }
            };
            

            【讨论】:

              猜你喜欢
              • 2018-05-23
              • 1970-01-01
              • 2010-09-16
              • 2010-12-17
              • 1970-01-01
              • 2012-06-05
              • 2021-08-19
              • 1970-01-01
              • 2015-10-26
              相关资源
              最近更新 更多