首页 > 解决方案 > std::shared_ptr 在 std::function 回调之间丢失

问题描述

我有以下异步任务实现

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

这里ptr=std::shared_ptralloc=std::make_shared

我的问题是在 textures::load 中的 on_success 事件中出现的异步任务结果为空,即使这不应该是可能的丢失并将 nullptr 发送到 on_success ...

标签: c++asynchronousc++-standard-library

解决方案


[&]通过引用捕获。[&]除非生成的 lambda 和所有副本将在当前范围结束之前被销毁,否则切勿使用。

您将它传递给do_async,这似乎不太可能在当前范围结束时破坏 lambdas(以及它们的所有副本)。

所以你的代码遵循悬空引用。

简单的更改是更改为[=]; 但实际上,当您进行异步工作时,请明确列出所有捕获。我什至建议不要捕获this.

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

我怀疑您的代码不是一个最小的完整可验证示例,并且正在发生其他事情。

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


推荐阅读