3

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

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

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

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

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

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

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

8 回答 8

5

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

}
于 2010-02-02T03:46:48.897 回答
3

由于这两个互斥类signals2::dummy_mutex并且boost::mutex不共享一个公共基类,您可以使用诸如“外部多态性”之类的东西来允许对它们进行多态处理。然后,您可以将它们用作通用互斥锁/锁接口的锁定策略。这允许您避免if在锁实现中使用“”语句。

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

于 2010-02-02T04:51:10.170 回答
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 {};

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

于 2010-02-02T08:38:21.937 回答
1

这是我的解决方案:

std::unique_lock<std::mutex> lock = dummy ?
     std::unique_lock<std::mutex>(mutex, std::defer_lock) :
     std::unique_lock<std::mutex>(mutex);
于 2017-09-14T13:48:39.657 回答
0

这还不够吗?

   class SomeClass
    {
    public:
        SomeClass(void);
        ~SomeClass(void);
        void Work(bool isMultiThreaded = false)
        {
            if(isMultiThreaded)
           {
               lock // mutex lock ...
               {
                    DoSomething
               }
           }
           else
           {
                DoSomething();
           }
       }   
    };
于 2010-02-02T02:59:56.790 回答
0

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

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

于 2010-02-02T07:43:28.917 回答
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);
}
于 2010-02-02T20:27:08.560 回答
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;
    }
};
于 2021-02-11T14:13:27.187 回答