使用weak_ptr的观察者模式
Observer pattern using weak_ptr
我试图从观察者模式编写一个安全的Subject
类。我想知道使用weak_ptr
是否是以以下方式存储IObserver
实例的最佳方式:
-
IObserver
实例被释放后不能再使用。 -
Subject
类没有保持应该被释放的IObserver
引用(失效侦听器问题)。
Subject
类必须是线程安全的。不幸的是,我们的编码标准规定不允许使用boost。我想我前世是个坏人。幸运的是,我被允许使用c++ 11 (Visual Studio 2012附带的)。
下面是一个示例Observer
类。
// Observer interface that supports notify() method
class IObserver
{
public:
virtual void notify() const = 0;
virtual ~IObserver() {}
};
// Concrete observer implementation that prints a message
class Observer : public IObserver
{
public:
Observer( const std::string& message) : m_message( message ){}
void notify() const {
printf( "%srn", m_message.c_str() );
}
private:
std::string m_message;
};
这是Subject
类。
// Subject which registers observers and notifies them as needed.
class Subject
{
public:
// Use shared_ptr to guarantee the observer is valid right now
void registerObserver( const std::shared_ptr<IObserver>& o )
{
std::lock_guard<std::mutex> guard( m_observersMutex );
m_observers.push_back( o );
}
void unregisterObserver( const std::shared_ptr<IObserver>& o )
{
std::lock_guard<std::mutex> guard( m_observersMutex );
// Code to remove the observer from m_observersMutex
}
// This is a method that is run in its own thread that notifies observers of some event
void doNotify()
{
std::lock_guard<std::mutex> guard( m_observersMutex );
// Notify any valid observers of events.
std::for_each( m_observers.cbegin(), m_observers.cend(),
[]( const std::weak_ptr<IObserver>& o )
{
auto observer = o.lock();
if ( observer ) {
observer->notify();
}
} );
// Remove any dead observers. These are ones which have expired().
m_observers.erase( std::remove_if( m_observers.begin(), m_observers.end(),
[]( const std::weak_ptr<IObserver>& o )
{
return o.expired();
} ), m_observers.end() );
}
private:
std::vector<std::weak_ptr<IObserver>> m_observers;
std::mutex m_observersMutex;
};
下面是一些执行Subject
的代码:
int main(int argc, wchar_t* argv[])
{
Subject subject;
auto observerHello = std::make_shared<Observer>( "Hello world" );
subject.registerObserver( observerHello );
{
// Create a scope to show unregistration.
auto observerBye = std::make_shared<Observer>( "Good bye" );
subject.registerObserver( observerBye );
subject.doNotify();
}
printf( "%srn", "Observer good bye is now be destructed" );
subject.doNotify();
return 0;
}
我使用weak_ptr
线程安全吗?从这里https://stackoverflow.com/a/2160422/1517648我认为是。
这是解决失效侦听器问题的合法方法吗?
我会有点怀疑你的doNotify
-假设在一个观察者你火最终添加或删除观察者?——不好的事情会发生(包括崩溃)。或者阻塞了另一个线程的动作,谁阻塞了试图添加观察者?——不好的事情发生了(死锁!)
这个问题很难解决。基本上,这是一个可重入性的问题。
当你持有锁时,永远不要放弃对代码的控制。在调用回调时持有锁是不允许的。所以,至少:
锁定,然后复制您的列表,然后解锁。在复制的同时,你也可以删除过期的观察者(从原始列表和复制列表中)。
然后从复制的列表中释放观察者。
这留下了一些未解决的问题。例如,删除一个观察者并不能保证它将来不会被调用!它只是意味着最终它不会被调用。
这有多重要取决于你如何使用倾听。
一种可能有效的方法是一个包含add/remove/notify/killthread事件的任务队列(将killthread设置为队列中的任务可以使关闭变得不那么烦人)。现在所有同步都在队列中。如果不打算编写非阻塞无锁队列,notify代码可以简单地锁定、std::move
队列、解锁,然后继续执行它。或者你可以写一个队列,pop
阻塞,直到有东西可读,而push
不阻塞。
一个快速而肮脏的"复制和广播"可能是这样的:
std::vector<std::shared_ptr<IObserver>> targets;
{
std::lock_guard<std::mutex> guard( m_observersMutex );
m_observers.erase( std::remove_if( m_observers.begin(), m_observers.end(),
[&targets]( const std::weak_ptr<IObserver>& o )
{
std::shared_ptr<IObserver> ptr = o.lock();
if (ptr) {
targets.push_back(ptr);
return false;
} else {
return true;
}
} ), m_observers.end() );
}
for( auto& target:targets ) {
target->notify();
}
- 如何设计具有不同类型的通知和观察器的观察者模式?
- 反射 + 函数指针与观察者模式
- 观察者模式不起作用
- 观察者模式:为什么主题应该是抽象的?
- 观察者模式专业化
- 如何在不必绑定到特定类的情况下实现观察者模式
- C++,函数指针与观察者模式
- C++11观察者模式(信号、插槽、事件、更改广播器/侦听器,或任何您想称之为的东西)
- 实施观察者模式C
- C++自己的观察者模式
- 不同可观察量的观察者模式
- 通过Boost信号的观察者模式2
- 具有类型信息的观察者模式(C++)
- 代码设计:观察者模式
- 观察者模式和继承:未调用正确的函数
- C++和Qt:观察者模式错误
- 在C++中实现观察者模式
- 多少听众是太多的观察者模式
- 当观察者希望观察不同的项目时实现观察者模式
- 通用观察者模式