Non-deterministic read caused by concurrent write can be demonstrated by the following code:
class Bar {
int val_;
std::mutex m_;
public:
explicit Bar(int v) : val_(v) {}
int val() {
std::lock_guard<std::mutex> l(m_);
return val_;
}
void set(int v) {
std::lock_guard<std::mutex> l(m_);
val_ = v;
}
};
class Foo {
Bar* bar_;
public:
explicit Foo(Bar* bar) : bar_(bar) {}
int val() const { return bar_.val(); }
};
Bar bar(1);
Foo foo(&bar);
cout << foo.val() << endl; // 1
bar.set(2); // probably executed in another thread
cout << foo.val() << endl; // 2
I am interested to know what are the design patterns / coding practices to avoid such problem.
One solution I can think of is to make a "deep copy", i.e. Foo will have a consistent snapshot of data it depends on, so all reads will get the same result. If Foo owns Bar, we can make Bar ref counted, and let Foo maintain a list of different versions of Bar (all mutation to Bar go through Foo), and client only get a "snapshot view" of Foo which only have access to one version of Bar, i.e. a consistent view. However, if Foo does not own Bar, it becomes a mess and I cannot think of a good solution here.
Aucun commentaire:
Enregistrer un commentaire