英文:
Efficient thread safe observer design pattern without expensive copying
问题
实现线程安全的观察者设计模式的高效方式是使用 C++ 的标准库提供的 std::shared_mutex
(或 std::mutex
)来管理观察者列表,以及使用智能指针(如 std::shared_ptr
)来管理观察者对象,以避免悬挂指针问题。以下是一个改进的示例代码:
#include <shared_mutex>
#include <vector>
#include <memory>
class Subject {
public:
void addObserver(std::shared_ptr<Observer> observer) {
std::unique_lock<std::shared_mutex> lock(mutex_);
observers_.emplace_back(std::move(observer));
}
void removeObserver(std::shared_ptr<Observer> observer) {
std::unique_lock<std::shared_mutex> lock(mutex_);
observers_.erase(std::remove(observers_.begin(), observers_.end(), observer), observers_.end());
}
void notifyObservers() {
std::shared_lock<std::shared_mutex> lock(mutex_);
for (const auto& observer : observers_) {
observer->update();
}
}
private:
mutable std::shared_mutex mutex_;
std::vector<std::shared_ptr<Observer>> observers_;
};
这种方式使用了共享锁和独占锁来保证线程安全,同时使用智能指针管理观察者对象,确保不会出现悬挂指针问题。这是一种更高效和安全的实现方式。
英文:
What's the efficient way to implement a thread safe observer design pattern ?
Using the following code:
#include <mutex>
#include <vector>
class Subject {
public:
void addObserver(Observer* observer) {
std::lock_guard<std::mutex> lock(mutex_);
observers_.push_back(observer);
}
void removeObserver(Observer* observer) {
std::lock_guard<std::mutex> lock(mutex_);
// Remove the observer from the vector
}
void notifyObservers() {
std::lock_guard<std::mutex> lock(mutex_);
for (Observer* observer : observers_) {
observer->update();
}
}
private:
std::mutex mutex_;
std::vector<Observer*> observers_;
};
it's not recommended to hold a lock and call a callback !
Also coping the list of observers each time I send a notification I not efficient also.
Is there an efficient way to achieve thread safety.
答案1
得分: 1
vector在需要进行删除操作时并不是那么出色,所以我会使用std::list。
我过去也做过类似的事情,关键是将添加/删除的管理锁与实际通知分离。如果有问题,请提出。
#include <mutex>
#include <list>
#include <iostream>
struct Observer
{
void notify()
{
std::cout << "已通知\n";
}
};
class Subject
{
public:
// 保持要添加的观察者的列表,以便与notifyObservers可能正在迭代的观察者列表分开
void addObserver(Observer* observer)
{
std::scoped_lock<std::mutex> lock(add_mutex_);
observers_to_add_.push_back(observer);
}
// 保持要删除的观察者的列表,以便与notifyObservers可能正在迭代的观察者列表分开
void removeObserver(Observer* observer)
{
std::scoped_lock<std::mutex> lock(remove_mutex_);
observers_to_remove_.push_back(observer);
}
// 保持要删除的观察者的列表,以便与notifyObservers可能正在迭代的观察者列表分开
void notifyObservers()
{
// 一个锁以防止多个线程同时访问通知逻辑
std::scoped_lock<std::mutex> lock(notify_mutex_);
// 本地范围以保持在添加观察者时的短时间内锁定
{
std::scoped_lock<std::mutex> lock(add_mutex_);
observers_.insert(observers_.end(), observers_to_add_.begin(), observers_to_add_.end());
observers_to_add_.clear();
}
// 本地范围以短时间内锁定删除管理
{
std::scoped_lock<std::mutex> lock(remove_mutex_);
for (const auto& observer : observers_to_remove_)
{
observers_.remove(observer);
}
observers_to_remove_.clear();
}
for (auto& observer : observers_)
{
observer->notify();
}
}
private:
std::mutex remove_mutex_;
std. mutex add_mutex_;
std::mutex notify_mutex_;
std::list<Observer*> observers_;
std::list<Observer*> observers_to_add_;
std::list<Observer*> observers_to_remove_;
};
int main()
{
Observer observer1;
Subject subject;
subject.addObserver(&observer1);
subject.notifyObservers();
return 0;
}
英文:
vector isn't all that great when you have to do removals so I would use std::list.
I've done similar things in the past, and the thing is to decouple administration locks for adding/removing from the actual notification.
Please ask questions if you have them
#include <mutex>
#include <list>
#include <iostream>
struct Observer
{
void notify()
{
std::cout << "notified\n";
}
};
class Subject
{
public:
// keep a list of observers to add, so it is seperate
// from the observers list notifyObservers may be iterating over
void addObserver(Observer* observer)
{
std::scoped_lock<std::mutex> lock(add_mutex_);
observers_to_add_.push_back(observer);
}
// keep a list of observers to add, so it is seperate
// from the observers list notifyObservers may be iterating over
void removeObserver(Observer* observer)
{
std::scoped_lock<std::mutex> lock(remove_mutex_);
observers_to_remove_.push_back(observer);
}
// keep a list of observers to remove, so it is seperate
// from the observers list notifyObservers may be iterating over
void notifyObservers()
{
// one lock to prevent multiple threads
// from concurrently accessing notification logic
std::scoped_lock<std::mutex> lock(notify_mutex_);
// local scope to keep lock on adding observers short
{
std::scoped_lock<std::mutex> lock(add_mutex_);
observers_.insert(observers_.end(), observers_to_add_.begin(), observers_to_add_.end());
observers_to_add_.clear();
}
// local scope for short lock on removal administration
{
std::scoped_lock<std::mutex> lock(remove_mutex_);
for (const auto& observer : observers_to_remove_)
{
observers_.remove(observer);
}
observers_to_remove_.clear();
}
for (auto& observer : observers_)
{
observer->notify();
}
}
private:
std::mutex remove_mutex_;
std::mutex add_mutex_;
std::mutex notify_mutex_;
std::list<Observer*> observers_;
std::list<Observer*> observers_to_add_;
std::list<Observer*> observers_to_remove_;
};
int main()
{
Observer observer1;
Subject subject;
subject.addObserver(&observer1);
subject.notifyObservers();
return 0;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论