将shared_ptr传递给线程

时间:2014-04-29 22:57:12

标签: c++ c++11 shared-ptr

我有以下代码:

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));

2 个答案:

答案 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架构。