目的
socket_.async_read_some(boost::asio::buffer(data_),[](){
... });
用asio实现网络编程通常的套路是单线程异步,对特定socket发起异步的读取操作,读取ok之后马上再次发起对这个socket上的异步写入操作,反复执行; client通过传入异步函数回调函数来发起下一次的读/取操作, 而可调用对象(函数对象/lambda表达式)都可以等加成传入了一个类实例,asio负责对这个类实例存储和释放,默认的内存分配策略是operator new
, 可以想象, 在持续不断的异步调用的时候,内存的分配和释放也在持续进行,降低了性能开销也产生了内存碎片. 针对这个优化点,asio提供了内存分配的hook,可以用client内存分配策略替换系统默认的行为.
实现原理
asio用于定制化内存分配策略函数如下:
以asio_handler_allocate
举例,my_handler就是回调对象的指针,可以看到,asio默认的内分分配策略是operator new
.
class my_handler;
void* asio_handler_allocate(std::size_t size, my_handler* context) {
return ::operator new(size);
}
如果想实现针对回调函数内存分配策略的定制,需要干两个活儿:
- 回调对象正常实现原本的回调操作,这部分可以通过
operator ()
完成,异步操作完成后就会执行真是操作. - 实现
asio_handler_allocate
和asio_handler_deallocate
用于定制化内存分配策略.asio会对其进行调用.
实现解析
官方例子地址如下:Allocation
handler_allocator
自定义内存分配策略的对象,提供内存分配和释放(allocate/deallocate)的成员函数.
// class handler_allocator
// 提供字节对齐的内存地址,此空间分配在栈上, 用于提供给可调用对象实际的存储空间
typename std::aligned_storage<1024>::type storage_;
// 空间是否在使用标记位
bool in_use_;
// 用于传递至回调函数内,以供调用,deallocate实现类似
void* allocate(std::size_t size)
{
if (!in_use_ && size < sizeof(storage_))
{
in_use_ = true;
return &storage_;
}
else
{
// 如果内存已经被使用,则使用默认分配策略
return ::operator new(size);
}
}
custom_alloc_handler
支持内存分配策略的回调对象包装类. 注释中给出解释:
// Handler回调函数通用化,放到模板里面
template <typename Handler>
class custom_alloc_handler{
public:
// 传入内存分配器 和 回调对象
custom_alloc_handler(handler_allocation &a, Handler h) :
allocator_(a),
handler_(h) {}
// 回调函数调用
template <typename ...Args>
void operator()(Args&&... args)
{
handler_(std::forward<Args>(args)...);
}
};
...
private:
// 保证所有的异步调用给回调函数分配的空间是同一份,所以声明为引用.
handler_allocator &allocator_;
Handler handler_;
这样,回调函数包装类就实现了对真实回调函数的封装,异步完成执行后,asio会调用包装类的operator ()
实现对真实回调函数的调用. 那么这个类如何利用内存分配器提供内存分配定制呢?
按照原理章节中描述,需要实现对两个自由函数的重载,仍然以内存分配函数为例,内存释放道理一样:
template<typename Handler>
void* asio_handler_allocate(std::size_t size, custom_alloc_handler<Handler>* this_handler)
{
// 调用回调封装对象中的内存分配器,实现定制化的内分分配策略.
return this_handler->allocator_.allocate(size);
}
custom_alloc_handler
辅助函数,用于构建实例类对象
template <typename Handler>
inline custom_alloc_handler<Handler> make_custom_alloc_handler(
handler_allocator& a, Handler h)
{
return custom_alloc_handler<Handler>(a, h);
}
customized Allocation使用
以上章节实际上是对client端程序员不可见的,当构建一个异步的应用程序且需要对内存分配进行控制(空间复用),那么使用方式如下:
void do_read()
{
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(data_),
// 把内存分配器兑现刚和真实的回调函数(这里用lambda)封装于customer_alloc_handler中
make_custom_alloc_handler(allocator_,
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
do_write(length);
}
}));
}
其中,allocator
为外部声明的内存分配器对象,因为需要给所有的异步调用使用,因此需要保证异步调用作用域内有效; 可以看到,对于客户端来说,可变的部分只有异步调用async_read_some
的第二个参数,其他地方没有改变,allocator
传递给封装类,供重载的自由内存分配函数调用, 封装类的第二个参数传入,作为异步操作结束后调用的回调函数.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。