代码之家  ›  专栏  ›  技术社区  ›  Tim Sylvester

在运行时选择互斥体或虚拟互斥体

  •  2
  • Tim Sylvester  · 技术社区  · 15 年前

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

    理想情况下,我会 shared_ptr<something> 并指定一个真实或假的互斥对象。然后我会“锁定”这个,不管里面有什么。

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

    现在有一个 signals2::dummy_mutex 但它不与 boost::mutex .

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

    在你指出替代方案之前:

    • 我可以在编译时选择一个实现,但是预处理器宏是丑陋的,维护项目配置对我们来说是很痛苦的。
    • 在多线程环境中,类的用户不希望承担锁定类的使用的责任,而希望类在内部进行自己的锁定。
    • “线程安全包装器”涉及的API和现有用法太多,无法成为实用的解决方案。
    7 回复  |  直到 7 年前
        1
  •  4
  •   Michael Anderson    15 年前

    像这样的怎么样? 它未经测试,但应该接近正常。 您可以考虑让模板类保存一个值而不是指针 如果互斥体支持正确的构造类型。否则,您可以专门化mymutex类以获得值行为。

    另外,它也不小心复制或销毁。我把它作为练习留给读者;)(共享或存储值而不是指针应该可以解决这个问题)

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

    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 );
         ...
       }
    
    }
    
        2
  •  3
  •   Void - Othman    15 年前

    因为这两个互斥类 signals2::dummy_mutex boost::mutex 不要共享一个公共的基类,你可以使用类似的 external polymorphism 允许他们接受多态性治疗。然后你就把它们当作锁 strategies 对于一个通用的互斥锁接口。这允许您避免使用“ if “锁实现中的语句。

    注释 :这基本上就是迈克尔提出的解决方案所实现的。我建议你接受他的回答。

        3
  •  1
  •   Matthieu M.    15 年前

    你听说过吗 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 {};
    

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

        4
  •  0
  •   Jagannath    15 年前

    这还不够吗?

       class SomeClass
        {
        public:
            SomeClass(void);
            ~SomeClass(void);
            void Work(bool isMultiThreaded = false)
            {
                if(isMultiThreaded)
               {
                   lock // mutex lock ...
                   {
                        DoSomething
                   }
               }
               else
               {
                    DoSomething();
               }
           }   
        };
    
        5
  •  0
  •   Sliq    15 年前

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

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

        6
  •  0
  •   Tim Sylvester    15 年前

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

    我去掉了抽象基类,将其与no-op“dummy”实现合并。还注意到 shared_ptr -具有隐式转换运算符的派生类。我觉得有点太棘手了,但它让我 shared_ptr<IMutex> 我以前使用过的对象 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
  •  0
  •   tsilvestre    7 年前

    这是我的解决方案:

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