我有一个类在其构造函数中使用std::async
调用异步任务来加载其内容。 (我希望以异步方式加载对象)
代码如下所示:
void loadObject(Object* object)
{
// ... load object
}
Object::Object():
{
auto future = std::async(std::launch::async, loadObject, this);
}
我在主线程上创建并删除了这些对象的几个实例,它们可以随时删除,甚至在加载完成之前就已删除。
我想知道在另一个线程上处理对象时是否有对象被破坏是危险的。如果对象被破坏,我怎么能停止线程?
编辑:由于bug,std::future
析构函数不会使用我正在使用的VS2013编译器阻止我的代码。
答案 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
标志