析构函数和异步任务

时间:2015-04-11 21:45:27

标签: c++ multithreading asynchronous concurrency

我有一个类在其构造函数中使用std::async调用异步任务来加载其内容。 (我希望以异步方式加载对象)

代码如下所示:

void loadObject(Object* object)
{
 // ... load object
}

Object::Object(): 
{
    auto future = std::async(std::launch::async, loadObject, this);
}

我在主线程上创建并删除了这些对象的几个实例,它们可以随时删除,甚至在加载完成之前就已删除。

我想知道在另一个线程上处理对象时是否有对象被破坏是危险的。如果对象被破坏,我怎么能停止线程?

编辑:由于bugstd::future析构函数不会使用我正在使用的VS2013编译器阻止我的代码。

3 个答案:

答案 0 :(得分:3)

您的代码中没有异步发生,因为构造函数阻塞直到loadObject()返回(由std::async返回的未来的析构函数隐式连接)。

如果不能,那将取决于你编写代码的方式(特别是你的析构函数),但很可能,你的代码会产生未定义的行为。

答案 1 :(得分:3)

正如MikeMB已经提到的,你的构造函数在加载完成之前还没有完成。请查看此问题,了解如何解决此问题:Can I use std::async without waiting for the future limitation?

  

我想知道在另一个线程上处理对象时是否有对象被破坏是危险的。

删除后访问对象的内存肯定是危险的,是的。行为将是未定义的。

  

如果对象被破坏,我怎么能停止线程呢?

我建议你首先要注意的是确保对象没有被破坏,而它仍然被指向的东西指向用它。

一种方法是使用一个成员标志,表示在异步任务中更新并在析构函数中检查的完成负载,并将访问与条件变量同步。这将允许析构函数阻塞,直到异步任务完成。

一旦您设法阻止对象被销毁,您可以使用另一个synchronized成员标志来表示该对象正在被销毁,并且如果已设置则跳过加载。这会增加同步开销,但如果加载费用很高,则可能是值得的。

另一种避免阻塞析构函数的方法是将std::shared_ptr传递给异步任务,并要求所有Object实例归共享指针所有。这种限制可能不是很理想,你需要继承std::enable_shared_from_this来获取构造函数中的共享指针。

答案 2 :(得分:-1)

当对象在另一个线程上被处理时,对象被破坏是危险的

您可以根据需求和期望的行为实际实施许多策略。

我会在这里实现一种pimpl策略,这意味着所有实际数据都将存储在您的对象所持有的指针中。您将所有数据加载到数据指针对象并将其存储在公共对象 atomically 中。

技术上说话的对象应该在构造完成之前完全构建并准备好使用。在您的情况下,数据指针对象仍可能无法使用。你应该让你的班级正确处理这种状态。

所以我们走了:

class Object
{
   std::shared_ptr<Object_data> d;
   Object::Object(): 
      d(std::make_shared<Object_data>())
   {
        some_futures_matser.add_future(std::async(std::launch::async, loadObject, d));

   }
}

然后在数据对象中创建原子标记,表示加载已完成且对象已准备好使用。

class Object_data
{
    // ...
    std::atomic<bool> loaded {false};
};

loadObject(std::shared_ptr<Object_data> d)
{
    /// some load code here
    d->loaded = true;
}

每次访问时都需要检查对象是否受到攻击(使用线程安全方式)loaded标志