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

时间:2010-02-02 02:48:50

标签: c++ multithreading mutex boost-thread

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

理想情况下,我会有一个shared_ptr<something>并分配一个真实或伪造的互斥对象。然后,我会“锁定”这个,而不考虑其中的内容。

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

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

那么,在没有使锁/保护代码比上面的例子更复杂的情况下,在真实互斥体和虚拟互斥体(信号2中的那个)之间进行选择的优雅方式是什么?

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

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

8 个答案:

答案 0 :(得分:4)

这样的事情怎么样? 它未经测试但应该接近OK。 您可以考虑使模板类保持值而不是指针 如果你的互斥体支持正确的结构。否则,您可以专门化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 );
     ...
   }

}

答案 1 :(得分:3)

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

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

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

这还不够吗?

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

答案 4 :(得分:0)

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

在Windows中,Critical Section 的单线程实现是虚拟实现。不确定你正在使用什么平台。

答案 5 :(得分:0)

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

我取消了抽象基类,将其与无操作“虚拟”实现合并。还要注意带有隐式转换运算符的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);
}

答案 6 :(得分:0)

这是我的解决方案:

n * n-1 * n-2. ...

答案 7 :(得分:0)

基于策略的选项:

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;
    }
};