You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++ Asio异步对象生命周期维护:栈对象独立销毁的优雅方案

Asio中栈上异步对象的安全销毁方案

在Asio中使用执行异步操作的对象时,传统回调式异步操作必须用shared_ptr管理对象生命周期——因为异步操作的handler会驻留在io_context中,即便对象本身被销毁,handler仍可能被调用,导致访问已释放的内存。

传统回调模式的问题示例

#include <bits/stdc++.h>
#include <asio.hpp>
using namespace std;

asio::io_context ctx;

struct Session {
    ~Session() {
        *stopped = true;
        cout << "~Session()" << endl;
    }
    std::shared_ptr<bool> stopped = std::make_shared<bool>(false);
    asio::steady_timer t{ctx};
    void async_operation() {
        t.expires_after(1s);
        t.async_wait([this, stopped = stopped](const asio::error_code &error) {
            cout << error.value() << ' ' << error.message() << endl;
            cout << "*stopped: " << *stopped << endl;
        });
    }
};

int main() {
    cout << boolalpha;
    
    {
        Session s;
        s.async_operation();
        ctx.run_for(100ms);
        cout << "use_count: " << s.stopped.use_count() << endl;
    }
    
    ctx.run_for(2s);
}

输出:

use_count: 2
~Session()
125 Operation aborted.
*stopped: true

协程场景的疑问

如果把异步操作改成协程形式,是否必须传递shared_ptr?能否设计成更简洁的接口,比如:

struct Session {
    auto connect(std::string_view host, std::string_view port) -> asio::awaitable<void>;
};

用户尝试通过转发shared_ptr实现协程版操作,但方案略显繁琐:

#include <bits/stdc++.h>
#include <asio.hpp>
using namespace std;
inline constexpr auto use_tuple_awaitable = asio::as_tuple(asio::use_awaitable);

asio::io_context ctx;

struct Session {
    ~Session() {
        *stopped = true;
        cout << "~Session" << endl;
    }
    std::shared_ptr<bool> stopped = std::make_shared<bool>(false);
    asio::steady_timer t{ctx};
    auto async_operation_impl(std::shared_ptr<bool> stopped_) -> asio::awaitable<void> {
        t.expires_after(1s);
        auto &&[error] = co_await t.async_wait(use_tuple_awaitable);
        cout << error.value() << ' ' << error.message() << endl;
        cout << "*stopped: " << *stopped_ << endl;
    }
    auto async_operation() -> asio::awaitable<void> {
        return async_operation_impl(stopped);
    }
};

int main() {
    cout << boolalpha;
    
    {
        Session s;
        asio::co_spawn(asio::system_executor(), s.async_operation(), asio::detached);
        cout << "use_count: " << s.stopped.use_count() << endl;
    }
    
    ctx.run_for(2s);
}

输出:

use_count: 2
~Session()
125 Operation aborted.
*stopped: true

正确实现方式:协程生命周期绑定+主动取消

要实现栈上生存、可独立于io_context销毁的异步对象,核心是让协程的生命周期与对象绑定,并在对象销毁时主动取消所有未完成的异步操作,无需额外传递shared_ptr。

优化后的协程版实现

#include <bits/stdc++.h>
#include <asio.hpp>
using namespace std;
inline constexpr auto use_tuple_awaitable = asio::as_tuple(asio::use_awaitable);

asio::io_context ctx;

struct Session {
    ~Session() {
        cout << "~Session" << endl;
        // 对象销毁时主动取消定时器
        t.cancel();
    }

    asio::steady_timer t{ctx};

    auto async_operation() -> asio::awaitable<void> {
        t.expires_after(1s);
        auto &&[error] = co_await t.async_wait(use_tuple_awaitable);
        // 仅在未被取消时处理结果
        if (!error || error != asio::error::operation_aborted) {
            cout << error.value() << ' ' << error.message() << endl;
        } else {
            cout << "异步操作已被取消" << endl;
        }
    }
};

int main() {
    cout << boolalpha;
    
    {
        Session s;
        // 协程绑定到Session对象的生命周期,对象销毁时协程会收到取消信号
        asio::co_spawn(ctx.get_executor(), [&s]() -> asio::awaitable<void> {
            co_await s.async_operation();
        }, asio::detached);
        
        ctx.run_for(100ms);
    }
    
    ctx.run_for(2s);
}

输出:

~Session
异步操作已被取消

关键要点

  1. 主动取消异步操作:在对象析构函数中调用异步操作对象(如steady_timer)的cancel()方法,Asio会立即触发协程的完成回调,并返回operation_aborted错误码。
  2. 协程与对象生命周期绑定:通过lambda捕获栈上对象的引用,当对象被销毁时,异步操作被取消,协程会收到错误并自然结束,不会访问已释放的对象内存。
  3. 无需shared_ptr:只要保证在协程执行期间对象未被销毁(或销毁时主动取消),就可以安全使用栈上对象,避免智能指针的繁琐。

扩展:多异步操作的统一管理

如果对象包含多个异步操作(如socket、定时器等),可以将所有异步操作对象统一管理,在析构时批量调用cancel(),确保所有未完成的操作都被终止,避免悬空引用。

内容的提问来源于stack exchange,提问作者Tellegar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 23:09:59