高效的线程安全观察者设计模式,无需昂贵的复制。

huangapple go评论68阅读模式
英文:

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 &lt;mutex&gt;
#include &lt;vector&gt;

class Subject {
public:
    void addObserver(Observer* observer) {
        std::lock_guard&lt;std::mutex&gt; lock(mutex_);
        observers_.push_back(observer);
    }

    void removeObserver(Observer* observer) {
        std::lock_guard&lt;std::mutex&gt; lock(mutex_);
        // Remove the observer from the vector
    }

    void notifyObservers() {
        std::lock_guard&lt;std::mutex&gt; lock(mutex_);
        for (Observer* observer : observers_) {
            observer-&gt;update();
        }
    }

private:
    std::mutex mutex_;
    std::vector&lt;Observer*&gt; 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 &lt;mutex&gt;
#include &lt;list&gt;
#include &lt;iostream&gt;
struct Observer
{
void notify() 
{
std::cout &lt;&lt; &quot;notified\n&quot;;
}
};
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&lt;std::mutex&gt; 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&lt;std::mutex&gt; 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&lt;std::mutex&gt; lock(notify_mutex_);
// local scope to keep lock on adding observers short    
{
std::scoped_lock&lt;std::mutex&gt; 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&lt;std::mutex&gt; lock(remove_mutex_);
for (const auto&amp; observer : observers_to_remove_)
{
observers_.remove(observer);
}
observers_to_remove_.clear();
}
for (auto&amp; observer : observers_)
{
observer-&gt;notify();
}
}
private:
std::mutex remove_mutex_;
std::mutex add_mutex_;
std::mutex notify_mutex_;
std::list&lt;Observer*&gt; observers_;
std::list&lt;Observer*&gt; observers_to_add_;
std::list&lt;Observer*&gt; observers_to_remove_;
};
int main()
{
Observer observer1;
Subject subject;
subject.addObserver(&amp;observer1);
subject.notifyObservers();
return 0;
}

huangapple
  • 本文由 发表于 2023年6月29日 15:01:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/76578715.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定