我有一个在几个项目之间共享的类,它的一些用法是单线程的,有些是多线程的。单线程用户不希望使用互斥锁定的开销,并且多线程用户不希望自己进行锁定,并且希望能够选择以“单线程模式”运行。所以我希望能够在运行时在真实和“虚拟”互斥锁之间进行选择。
理想情况下,我会有一个shared_ptr<something>
并分配一个真实或伪造的互斥对象。然后,我会“锁定”这个,而不考虑其中的内容。
unique_lock<something> guard(*mutex);
... critical section ...
现在有一个signals2::dummy_mutex
但它没有与boost::mutex
共享一个公共基类。
那么,在没有使锁/保护代码比上面的例子更复杂的情况下,在真实互斥体和虚拟互斥体(信号2中的那个)之间进行选择的优雅方式是什么?
而且,在你指出替代方案之前:
答案 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_mutex
和boost::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;
}
};