I have the resource "resource" that wrapped in shared_ptr, and I want to access it from other threads. When I do this:
// foo.h
class Foo{
public:
std::shared_ptr<Setup> GetSomeThing();
void SetSomeThing();
private:
std::shared_ptr<Setup> resource;
std::mutex lock;
}
//Foo.cpp
std::shared_ptr<Setup> Foo::GetSomeThing()
{
std::lock_guard<std::mutex> lock (mutex);
return resource;
}
void Foo::SetSomeThing()
{
std::lock_guard<std::mutex> lock (mutex);
resource = ...;
}
is it all ok? When will be created a return object and when will be destroyed the lock? Is there something about it in the documentation? Thank you!
This answer assumes (for clarity) that the two lines:
std::lock_guard<std::mutex> lock (mutex);
are both replaced with
std::lock_guard<std::mutex> guard (lock);
If multiple threads access a single instance of std::shared_ptr<>
without synchronization and any of those call non-const
members then a data race occurs.
That means you must ensure synchronization between SetSomeThing()
and GetSomething()
.
Introducing a std::mutex
and using std::lock_guard<>
in the way proposed will do that. The returned copy will have been constructed before the destructor of guard
is called.
Do note that it's about the same instance. The copy returned by GetSomeThing()
has sufficient internal synchronization to ensure it can be accessed (non-const
and even destructed) without synchronizing other instances.
However none of that prevents data races on any shared Setup
object (part-)owned by the std::shared_ptr<Setup>
. If all access to Setup
is read-only then multiple threads can access it but if any threads write to the shared data a data race occurs (without further synchronization not shown in the question).
An object like Setup
in a simple application may be constructed and initialized before multiple threads are launched and destructed when all but the main-thread have terminated. In that specific case no further synchronization would be required and even the provided lock is superfluous.
Here's a non-normative reference:
http://en.cppreference.com/w/cpp/memory/shared_ptr
Refer to the last paragraph of the opening description.
Footnote: Changing application "setup" can be far harder than simply ensuring that data races don't occur on the attributes. Threads may need to pend adopting changes or 'abandon' activity. For example consider a graphical program in which the screen resolution is changed during a 'draw' step. Should it finish the draw and produce a canvas that is too large/small or dump the part-drawn canvas and adopt the new resolution? Has the setup even been acquired in such a way that the draw will produce something consistent with 'before' or 'after' and not some nonsensical (possibly crashing) hybrid?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With