我有以下代码:
shared_ptr<A> a;
B b(a);
a.reset(new A());
在B级我创建一个新线程并等待 a “准备好”:
B(shared_ptr<A> a) {
_a = a;
//create thred here
}
//...
//in thread:
while (_this->_a == NULL) {}
问题是,即使执行 a.reset(new A()); 行,在B中启动的线程仍然认为 _this-&gt; _a 是空的,永远不会离开while循环!
我尝试使用 a = make_shared(new A()); 或 a = shared_ptr(new A()); - 效果仍然相同。我还尝试将 a 作为 const引用传递 - 同样的事情。
最有趣的是,当我使用普通指针并将对该指针的引用传递给B类时,一切正常:
A* a;
B b(a);
a = new A();
//...
B(A*& a) {
_a = a;
//create thred here
}
//in thread:
while (_this->_a == NULL) {}
//In this case the loop is finished
我在这里想念什么?
更新(01.05.2014): 我用这样的代码解决了这个问题:
typedef shared_ptr<ClassA> ClassAPtr;
typedef shared_ptr<ClassB> ClassBPtr;
typedef shared_ptr<ClassC> ClassCPtr;
//...
ClassAPtr a(new ClassA());
ClassBPtr b( new ClassB() );
b->attachA(a);
a->attachB(b);
ClassCPtr c( new ClassC() );
c->attachB(b);
c->attachA(a);
c->run();
b->run();
a->run();
在每个类中我都有原子变量_running:
atomic<bool> _running;
此外,我在每个线程循环中都有以下同步代码:
while (
_this->_a == NULL || !_this->_a->isRunning()
||
_this->_b == NULL || !_this->_b->isRunning()
)
std::this_thread::sleep_for(std::chrono::milliseconds(500));
答案 0 :(得分:2)
您已将shared_ptr<A>
的副本传递给B
的实例。通过调用a
将新对象分配给reset
时,b
中存储的副本不知道发生了这种情况。这就是它永远不会看到更新的原因。
调用shared_ptr::reset
使shared_ptr
的实例放弃托管对象的所有权,这意味着如果该实例恰好是唯一所有者,则递减use_count
并销毁该对象。如果它不是唯一的所有者,那么管理该对象的其他shared_ptr
将负责管理其生命周期。在任何一种情况下,您调用的实例reset
现在可以自由获取您可能已作为参数传递的另一个对象的所有权。在您的情况下,这一切都更简单,因为最初没有对象被管理,但适用相同的逻辑。调用b
后,a
中存储的副本绝不会与reset
相关联。
假设您的班级A
实现了两个成员函数activate()
和is_active()
,具有明显的功能。还假设构建A
的实例会使其处于停用的状态,直到您调用activate()
。然后你可以解决这个问题如下:
auto a = make_shared<A>();
B b(a);
a->activate();
// within the thread
while (!_this->_a->is_active()) {}
即便如此,您还需要使用一些同步原语来防止数据竞争。例如,如果activate()
设置布尔数据成员,则该成员的类型应为std::atomic<bool>
或std::atomic_flag
,而不是普通bool
。这同样适用于可能从不同线程读取和写入的任何其他数据成员。
答案 1 :(得分:0)
std::shared_ptr
不是线程安全的。您需要使用不同的同步机制。 std::future
是一个可用于等待另一个线程的对象。您可以使用std::packaged_task
创建一个。
可能发生的是本地副本由共享指针的内部构成。此缓存副本不知道主内存中的更新。指针已更新,但这取决于哪个编译器,哪些优化是活动的以及CPU架构。