简体   繁体   中英

simplfied observer pattern with std::shared_ptr/weak_ptr

Here is a simplified observer pattern:

  1. one creator creates a profile when it starts and "destroy" it when it is done.
  2. zero, one or more observers try to "look at" the profile at any time.

To implement it, the trick is that observers shall refcnt profile, so the last observer (or creator) can safely destroy it.

I can do it without shared_ptr/weak_ptr, but I wonder if using them can avoid re-inventing wheels.

Here is my code:

#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
}

But the program crashes either at std::weak_ptr<Profile> weak = g_profile or assert(a == prof->a_) . So here are my questions:

  1. do you have a pointer implementing observer pattern (or variant) with shared_ptr/weak_ptr?
  2. what's wrong with the above code? Can you make it right?

You have undefined bahavior when one thread reads from the shared pointer g_profile (observer) while the other thread writes to it (when creator calls std::shared_ptr::reset )

If you want to use the shared_ptr from two threads you'll have to use a lock or atomic_shared_ptr .

Also volatile does not guarantee any synchronization as it does in java. See this answer .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM