析构函数和异步任务

Destructors and asynchronous tasks

我有一个 class,它在构造函数中使用 std::async 调用异步任务来加载其内容。 (我想异步加载对象)

代码如下所示:

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

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

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

我想知道当对象仍在另一个线程上处理时销毁对象是否危险。如果对象被销毁,我该如何停止线程?

编辑: std::future 析构函数不会阻止我使用 VS2013 编译器的代码,因为 bug.

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

如果不会,这将取决于您编写代码的方式(尤其是您的析构函数),但最有可能的是,您的代码会导致未定义的行为。

当对象仍在另一个线程上处理时销毁它是危险的

您实际上可以根据要求和期望的行为实施很多策略。

我会在这里实施某种 pimpl 策略,这意味着所有实际数据都将存储在您的对象所持有的指针中。您会将所有数据加载到数据指针对象,并将其存储在 public-对象中 原子地

从技术上讲,对象应该在构造器完成时完全构造并准备好使用。在您的情况下,数据指针对象可能仍未准备好使用。你应该让你的 class 正确处理那个状态。

我们开始吧:

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 标志

访问对象(使用线程安全方式)时,您必须检查对象是否被构造

正如 MikeMB 已经提到的,您的构造函数在加载完成之前不会完成。检查此问题以了解如何克服该问题:Can I use std::async without waiting for the future limitation?

I'd like to know if it is dangerous to having object getting destroyed when it is still getting handled on another thread.

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

how can I stop the thread if the object gets destroyed ?

我建议您首先注意的是,确保对象不会在仍被将要使用的对象指向时被销毁它。

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

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

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