【问题标题】:simplfied observer pattern with std::shared_ptr/weak_ptr使用 std::shared_ptr/weak_ptr 的简化观察者模式
【发布时间】:2020-07-22 01:09:19
【问题描述】:

这是一个简化的观察者模式:

  1. 创建者在启动时创建配置文件,并在完成时“销毁”它。
  2. 零,一个或多个观察者随时尝试“查看”配置文件。

要实现它,诀窍是观察者应该 refcnt 配置文件,因此最后一个观察者(或创建者)可以安全地销毁它。

不用shared_ptr/weak_ptr我也能做到,但我想知道使用它们是否可以避免重新发明轮子。

这是我的代码:

#include <iostream>
#include <memory>
#include <thread>
#include <cassert>

volatile bool playing = true;

class Profile {
public:
    int a_;
    Profile(int v) {a_ = v;}
};

std::shared_ptr<Profile> g_profile{ nullptr };

void observer() {
    do {
        // observe profile if I can
        std::weak_ptr<Profile> weak = g_profile;
        if (auto prof = weak.lock()) {
            auto a = prof->a_;
            // if prof is stable, I shall see the same a_
            assert(a == prof->a_);
        }
        else {
            std::cout << ".";
        }
    } while (playing);
}

void creator() {
    do {
        // create profile when I start
        g_profile.reset(new Profile(std::rand()));
        std::weak_ptr<Profile> weak = g_profile;
        assert(weak.lock() != nullptr);
        
        // doing some work ...

        // destroy profile when I am done
        g_profile.reset();
    } while (playing);
}

void timer() {
    std::this_thread::sleep_for(std::chrono::seconds(10));
    playing = false;
}

int main() {
    std::thread cr{ creator };
    std::thread ob{ observer };
    std::thread tm{ timer };
    cr.join();ob.join();tm.join();

    // no memory leak
}

但程序崩溃要么在 std::weak_ptr&lt;Profile&gt; weak = g_profileassert(a == prof-&gt;a_)。所以这是我的问题:

  1. 您是否有使用 shared_ptr/weak_ptr 实现观察者模式(或变体)的指针?
  2. 上面的代码有什么问题?你能做对吗?

【问题讨论】:

  • 您可能正在重新发明 Boost Signals2。 (Signals1 用于 C++98。Signals2 用于 C++11。)
  • @Eljay,我不确定升压信号是否是我正在寻找的信号。我正在寻找上述用法的 shared_ptr/weak_ptr 实现。
  • 这是一个正确的检查:if (auto prof = weak.lock()) { 看起来您正在分配 if
  • @Ilian Zapryanov,支票是if (nullptr != (...))的简化形式
  • @IlianZapryanov • 可以在ifhere 中声明变量以获取有关它的博客文章。

标签: c++ concurrency


【解决方案1】:

当一个线程从共享指针g_profile(观察者)读取而另一个线程向它写入时(当创建者调用std::shared_ptr::reset)时,您拥有undefined bahavior

如果您想在两个线程中使用shared_ptr,您必须使用锁或atomic_shared_ptr

另外,volatile 不保证任何同步,就像它在 java 中所做的那样。见this answer

【讨论】:

  • 谢谢 - 看起来 shared_ptr 不是为读/写混合而设计的。 volatile 这里使用应该没问题,因为它是线程间的信号状态。
  • 在那个非常特殊的情况下, volatile 就足够了,因为它是一个取消标志。不过味道不好。
  • @curiousguy,您能否分享一下取消标志的解决方案?
  • @curiousguy 你确定吗? cppref 说“也就是说,在单个执行线程中,不能优化或重新排序易失性访问,并使用另一个可见的副作用进行排序,该副作用在易失性访问之前或之后排序。这使得易失性对象适合与信号处理程序,但不与另一个执行线程一起执行,请参阅 std::memory_order"。
  • @fatun 正确的方法是使用原子整数。标准类是std::atomic&lt;int&gt;。您可以使用宽松的操作 (memory_order_relax)。搜索stdatomic 标签。
猜你喜欢
  • 2013-01-01
  • 2017-01-23
  • 1970-01-01
  • 1970-01-01
  • 2016-02-20
  • 2023-04-10
  • 2023-03-06
  • 1970-01-01
  • 2022-12-14
相关资源
最近更新 更多