asio_example筆記之Allocation

目的

socket_.async_read_some(boost::asio::buffer(data_),[](){
    ... });

用asio實現網絡編程一般的套路是單線程異步,對特定socket發起異步的讀取操做,讀取ok以後立刻再次發起對這個socket上的異步寫入操做,反覆執行; client經過傳入異步函數回調函數來發起下一次的讀/取操做, 而可調用對象(函數對象/lambda表達式)均可以等加成傳入了一個類實例,asio負責對這個類實例存儲和釋放,默認的內存分配策略是operator new, 能夠想象, 在持續不斷的異步調用的時候,內存的分配和釋放也在持續進行,下降了性能開銷也產生了內存碎片. 針對這個優化點,asio提供了內存分配的hook,能夠用client內存分配策略替換系統默認的行爲.html

實現原理

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

若是想實現針對回調函數內存分配策略的定製,須要幹兩個活兒:編程

  1. 回調對象正常實現本來的回調操做,這部分能夠經過operator ()完成,異步操做完成後就會執行真是操做.
  2. 實現asio_handler_allocateasio_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

支持內存分配策略的回調對象包裝類. 註釋中給出解釋:socket

// 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 ()實現對真實回調函數的調用. 那麼這個類如何利用內存分配器提供內存分配定製呢?
按照原理章節中描述,須要實現對兩個自由函數的重載,仍然之內存分配函數爲例,內存釋放道理同樣:async

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傳遞給封裝類,供重載的自由內存分配函數調用, 封裝類的第二個參數傳入,做爲異步操做結束後調用的回調函數.

參考

boost::asio sample Allocation
關於內存對齊的面試題

相關文章
相關標籤/搜索