使用shared_ptr创建Boost.Mysql连接对象时触发访问违例问题排查
问题描述
我编写了一段用shared_ptr管理boost::mysql::tcp_ssl_connection的代码,希望连接在应用运行全程保持,程序结束时关闭。但调试中触发了访问违例错误,注释创建shared_ptr的代码后错误消失,请问是否是无法在内存中创建连接资源导致的?
代码实现
class DatabaseProcess : public MainframeComponent { public: // MainframeComponent dispatch module type is set to Main because DatabaseProcess doesn't run on its own thread. DatabaseProcess(boost::asio::io_context &io_context) : _io_context(io_context), MainframeComponent(Horizon::System::RUNTIME_DATABASE) { } ~DatabaseProcess() { // Close connection object. if (_connection != nullptr) { _connection->close(); _connection.reset(); } } void initialize(int segment_number = 1) override { HLog(error) << "Database not configured"; } void initialize(int segment_number, std::string host, int port, std::string user, std::string pass, std::string database) { try { boost::asio::ssl::context ssl_ctx(boost::asio::ssl::context::tls_client); _connection = std::make_shared<boost::mysql::tcp_ssl_connection>(_io_context.get_executor(), ssl_ctx); boost::asio::ip::tcp::resolver resolver(_io_context.get_executor()); auto endpoints = resolver.resolve(host, std::to_string(port)); boost::mysql::handshake_params params(user, pass, database); _connection->connect(*endpoints.begin(), params); } catch (boost::mysql::error_with_diagnostics &error) { HLog(error) << error.what(); } bool value = _is_initialized; _is_initialized.compare_exchange_strong(value, true); } void finalize(int segment_number = 1) override { bool value = _is_initialized; _is_initialized.compare_exchange_strong(value, false); } std::shared_ptr<boost::mysql::tcp_ssl_connection> get_connection() { return _connection; } bool is_initialized() { return _is_initialized.load(); } protected: std::shared_ptr<boost::mysql::tcp_ssl_connection> _connection{nullptr}; boost::asio::io_context &_io_context; std::atomic<bool> _is_initialized; };
调试输出
Exception thrown at 0x00007FFA63663FAA (ntdll.dll) in ZoneSystemTest.exe: 0xC0000005: Access violation writing location 0x0000000000000024.
The program '[27192] ZoneSystemTest.exe' has exited with code 0 (0x0).
触发错误的关键代码行:_connection = std::make_shared<boost::mysql::tcp_ssl_connection>(_io_context.get_executor(), ssl_ctx);
问题根源
并非无法创建连接资源导致的错误,核心问题在于ssl_ctx的生命周期不匹配:
- 在
initialize函数中,ssl_ctx是局部变量,函数执行完毕后会被销毁。 boost::mysql::tcp_ssl_connection的构造函数接受的是boost::asio::ssl::context的引用,意味着_connection会持有对这个已销毁局部变量的引用。后续任何对_connection的操作(比如析构时调用close())都会访问已释放的内存,直接触发访问违例。
此外还有两个潜在问题:
- 成员变量
_is_initialized未在构造函数中初始化,属于未定义行为,可能导致逻辑错误。 _connection->close()如果是异步操作,未等待完成就销毁连接,可能引发资源访问问题。
修复方案
1. 调整ssl_ctx的生命周期
将ssl_ctx提升为类的成员变量,确保其生命周期与_connection一致:
class DatabaseProcess : public MainframeComponent { public: DatabaseProcess(boost::asio::io_context &io_context) : _io_context(io_context), _ssl_ctx(boost::asio::ssl::context::tls_client), // 初始化SSL上下文 MainframeComponent(Horizon::System::RUNTIME_DATABASE), _is_initialized(false) // 初始化原子变量 { } // ... 其他成员函数 ... protected: std::shared_ptr<boost::mysql::tcp_ssl_connection> _connection{nullptr}; boost::asio::io_context &_io_context; std::atomic<bool> _is_initialized; boost::asio::ssl::context _ssl_ctx; // 新增成员变量 };
修改initialize函数,使用类成员的_ssl_ctx:
void initialize(int segment_number, std::string host, int port, std::string user, std::string pass, std::string database) { try { // 移除局部的ssl_ctx,使用类成员变量 _connection = std::make_shared<boost::mysql::tcp_ssl_connection>(_io_context.get_executor(), _ssl_ctx); boost::asio::ip::tcp::resolver resolver(_io_context.get_executor()); auto endpoints = resolver.resolve(host, std::to_string(port)); boost::mysql::handshake_params params(user, pass, database); _connection->connect(*endpoints.begin(), params); } catch (boost::mysql::error_with_diagnostics &error) { HLog(error) << error.what(); _is_initialized.store(false); // 初始化失败时重置状态 return; } _is_initialized.store(true); // 直接赋值,无需compare_exchange_strong(单线程初始化场景) }
2. 优化析构逻辑
如果close()是异步操作,需确保操作完成后再销毁连接:
~DatabaseProcess() { if (_connection != nullptr) { // 若close为异步操作,需同步等待完成(示例,具体需参考boost::mysql文档) boost::asio::post(_io_context.get_executor(), [this]() { _connection->close(); _connection.reset(); }); _io_context.run_one(); // 等待close操作完成 } }
内容的提问来源于stack exchange,提问作者SagunKho
相关产品推荐
相关产品推荐

