如何优化代码确保asio::async_send的底层内存块始终有效?
优化方案与改进建议
你的改写已经解决了核心的内存生命周期问题,但还有几个可以优化的点,让代码更安全、简洁且符合Boost.Asio的最佳实践:
1. 优化参数传递,避免意外修改原变量
你当前用std::string&作为参数,内部std::move(str)会清空原字符串,这会给调用者带来意外副作用。更好的做法是传值,利用C++移动语义减少拷贝,同时避免修改外部变量:
void Conversation::do_write(std::string str) // 改为传值 { auto conversation_ptr = shared_from_this(); std::shared_ptr<std::string> content_ptr = std::make_shared<std::string>(std::move(str)); m_socket.async_send(asio::buffer(*content_ptr), [this, conversation_ptr, content_ptr](std::error_code error, size_t length) { if (!error) { std::cout << "数据写入成功,字节数:" << length << std::endl; } else { std::cout << "写入数据出错:" << error.message() << std::endl; } }); }
调用者传左值时会触发一次拷贝,传右值时直接移动,语义清晰且不会意外修改原变量。
2. 解决异步写的并发问题(关键优化)
Boost.Asio的流套接字不允许同时发起多个异步写操作,多次调用原do_write会导致未定义行为。需维护写队列保证操作串行执行:
首先在Conversation类中添加成员变量:
std::queue<std::shared_ptr<std::string>> m_write_queue;
然后修改do_write并新增执行写操作的函数:
void Conversation::do_write(std::string str) { auto content_ptr = std::make_shared<std::string>(std::move(str)); bool was_empty = m_write_queue.empty(); m_write_queue.push(content_ptr); if (was_empty) { do_write_impl(); } } void Conversation::do_write_impl() { auto conversation_ptr = shared_from_this(); auto content_ptr = m_write_queue.front(); m_socket.async_send(asio::buffer(*content_ptr), [this, conversation_ptr, content_ptr](std::error_code error, size_t length) { m_write_queue.pop(); if (!error) { std::cout << "数据写入成功,字节数:" << length << std::endl; } else { std::cout << "写入数据出错:" << error.message() << std::endl; // 可在此处理连接断开等错误,比如关闭套接字 } // 队列非空则继续执行下一次写操作 if (!m_write_queue.empty()) { do_write_impl(); } }); }
所有写请求会排队执行,只有当前写操作完成后才会启动下一个,完全符合Boost.Asio的规范。
3. 简化捕获,避免冗余
lambda中this和conversation_ptr作用重复(均保证Conversation对象存活),可直接捕获shared_from_this(),无需同时捕获this:
// 在do_write_impl中 m_socket.async_send(asio::buffer(*content_ptr), [conversation_ptr, content_ptr](std::error_code error, size_t length) { conversation_ptr->m_write_queue.pop(); if (!error) { std::cout << "数据写入成功,字节数:" << length << std::endl; } else { std::cout << "写入数据出错:" << error.message() << std::endl; } if (!conversation_ptr->m_write_queue.empty()) { conversation_ptr->do_write_impl(); } });
避免捕获this带来的悬空风险,语义更清晰。
4. 可选:利用Boost.Asio动态缓冲区
不想手动管理shared_ptr的话,可使用asio::dynamic_buffer配合队列,底层内存由队列保证有效性:
// 成员变量改为存储string的队列 std::queue<std::string> m_write_queue; void Conversation::do_write(std::string str) { bool was_empty = m_write_queue.empty(); m_write_queue.push(std::move(str)); if (was_empty) { do_write_impl(); } } void Conversation::do_write_impl() { auto conversation_ptr = shared_from_this(); auto& content = m_write_queue.front(); m_socket.async_send(asio::dynamic_buffer(content), [this, conversation_ptr](std::error_code error, size_t length) { m_write_queue.pop(); if (!error) { std::cout << "数据写入成功,字节数:" << length << std::endl; } else { std::cout << "写入数据出错:" << error.message() << std::endl; } if (!m_write_queue.empty()) { do_write_impl(); } }); }
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

