Ste*_*eve 15 c++ smart-pointers std c++11
我正在尝试Subject
从观察者模式中编写一个安全类.我想知道使用weak_ptr
是否是以下列方式存储IObserver
实例的最佳方式:
IObserver
被释放后无法使用实例.Subject
类不抱上IObserver
应free'd引用(流逝的听众的问题).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( "%s\r\n", m_message.c_str() );
}
private:
std::string m_message;
};
Run Code Online (Sandbox Code Playgroud)
这是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;
};
Run Code Online (Sandbox Code Playgroud)
这是一些练习的代码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( "%s\r\n", "Observer good bye is now be destructed" );
subject.doNotify();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我使用weak_ptr
线程安全吗?从这里/sf/answers/151229571/我认为是.
这是解决失效监听器问题的合法方式吗?
Yak*_*ont 13
我对你有点怀疑doNotify
- 假设你发射的观察者最终会添加或删除观察者? - 坏事发生(包括崩溃).或阻止另一个线程的操作,谁阻止尝试添加观察者? - 坏事发生了(死锁!)
这很难解决.基本上,它是重入的问题.
永远不要在握住锁定时控制代码.在呼叫回叫时按住一个锁是一个禁忌.
所以,至少:
锁定然后复制您的列表然后解锁.在执行此复制时,您还可以删除过期的观察者(来自原始和副本列表).
然后从复制的列表中激活观察者.
这留下了一些未解决的问题.例如移除观察者并不能保证将来不会被调用!它只是意味着它最终不会被调用.
这有多重要取决于你如何使用听力.
可能有效的一种方法是包含add/remove/notify/killthread事件的任务队列(使killthread成为队列中的任务使得关闭更加烦人).现在所有同步都在队列中.如果您不打算编写非阻塞无锁队列,则通知代码可以简单地锁定,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();
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
3620 次 |
最近记录: |