std::shared_ptr在std::函数回调之间丢失



我有以下异步任务实现

template <class T>
class async_task {
public:
ptr<progress_status> do_async(std::function<void(ptr<T>)> on_success, std::function<void(ptr<progress_status>)> on_failure);
protected:
virtual void do_async_tick() = 0;
ptr<progress_status> status = nullptr;
ptr<T> result = nullptr;
};
template<class T>
inline ptr<progress_status> async_task<T>::do_async(std::function<void(ptr<T>)> on_success, std::function<void(ptr<progress_status>)> on_failure)
{
status = alloc<progress_status>();
std::async(std::launch::async, [&, this] {

do {
try {
do_async_tick();
}
catch (std::exception& e) {
status->error = alloc<std::string>(e.what());
}
} while (status->error != nullptr && result != nullptr);
if (status->error != nullptr)
on_failure(status);
else {
status->progress = 1.f;
on_success(result);
}
});
return status;
}

我实现了这个类,用它来加载一些纹理

class texture_loader_async : public async_task<std::vector<std::pair<std::string, ptr<dx_texture>>>> {
public:
virtual void do_async_tick() override;
};
class textures : public resource_bag<dx_texture>, public singleton<textures>
{
friend class singleton<textures>;
public:
virtual ptr<progress_status> load(std::function<void()> on_success, std::function<void()> on_failure) override;
private:
texture_loader_async loader;
};

随着的实施

ptr<progress_status> textures::load(std::function<void()> on_success, std::function<void()> on_failure)
{
return loader.do_async(
[&](auto textures) {
for (auto& pair : *textures) {
add_resource(pair.first.c_str(), pair.second);
}
on_success();
},
[&](auto status) {
on_failure();
}
);
}

这里ptrstd::shared_ptrallocstd::make_shared

我的问题是,textures::load中的on_ccess事件中的异步任务结果为null,尽管这不应该是可能的基本上在async_task::do_async中,while由于结果而结束!=nullptr,在途中的某个地方它丢失了,它将nullptr发送到on_ccess。。。

[&]通过引用捕获。除非生成的lambda和所有副本将在当前作用域结束之前销毁,否则永远不要使用[&]

您将它传递给do_async,它似乎不太可能在当前作用域结束时销毁Lambda(及其所有副本(。

因此,您的代码遵循悬空引用。

简单的更改是更改为[=];但实际上,当您进行异步工作时,会显式列出所有捕获。我甚至建议不要捕捉this

当然,在函数的主体中,您调用std::async并丢弃结果,结果会立即阻塞,直到异步任务完成。就是这样。但如果你解决了这个问题,你就会遇到引用捕获问题。

我怀疑你的代码不是一个最小的完全可验证的例子,还有其他事情正在发生

status->error代码也有臭味。异步代码和主线程之间似乎没有任何同步(我的意思是,也许你有一个疯狂的operator->重载,但我对此表示怀疑(。

尽管lambda捕获和阻塞异步已经存在问题(感谢您向我指出(,但导致该错误的主要问题是while条件反转。。。

while (status->error != nullptr && result != nullptr);

while (status->error == nullptr && result == nullptr);

最新更新