可以使用一些自定义分配器来避免这种分配吗?

Can this allocation be avoided with some custom allocator?

我正在尝试学习如何将 boost::asio 与自定义分配器一起使用。在下面的例子中,我安排了一个计时器在一个链上被触发,并且处理程序是使用自定义分配器分配的。为此,我使用了这个 example:。但即使我指定了一个自定义分配器,也会调用带有堆栈跟踪的 new:

operator new(unsigned long) 0x00007fff2040882a
std::__1::__libcpp_allocate(unsigned long, unsigned long) new:253
std::__1::allocator<std::__1::__shared_ptr_emplace<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, std::__1::allocator<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > > > >::allocate(unsigned long) memory:1664
std::__1::enable_if<!(is_array<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >::value), std::__1::shared_ptr<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > > >::type std::__1::make_shared<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >(boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >&&) memory:4037
void boost::asio::execution::detail::any_executor_base::construct_object<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >(boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >&, std::__1::integral_constant<bool, false>) any_executor.hpp:1155
boost::asio::execution::detail::any_executor_base::any_executor_base<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >(boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, std::__1::integral_constant<bool, false>) any_executor.hpp:502
boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >::any_executor<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >(boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, std::__1::enable_if<conditional<(!(is_same<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > >::value)) && (!(is_base_of<boost::asio::execution::detail::any_executor_base, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >::value)), boost::asio::execution::detail::is_valid_target_executor<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, void (boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> >)>, std::__1::integral_constant<bool, false> >::type::value, void>::type*) any_executor.hpp:1406
boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >::any_executor<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >(boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, std::__1::enable_if<conditional<(!(is_same<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > >::value)) && (!(is_base_of<boost::asio::execution::detail::any_executor_base, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >::value)), boost::asio::execution::detail::is_valid_target_executor<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, void (boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> >)>, std::__1::integral_constant<bool, false> >::type::value, void>::type*) any_executor.hpp:1409
boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > boost::asio::execution::detail::any_executor_base::prefer_fn_impl<boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> > >(void const*, void const*, std::__1::enable_if<(!(is_same<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, void>::value)) && (boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >::is_preferable), void>::type*) any_executor.hpp:1102
boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > boost::asio::execution::detail::any_executor_base::prefer_fn<boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> > >(void const*, void const*) any_executor.hpp:1115
boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >::prefer<boost::asio::execution::detail::blocking::possibly_t<0> >(boost::asio::execution::detail::blocking::possibly_t<0> const&, std::__1::enable_if<find_convertible_preferable_property<boost::asio::execution::detail::blocking::possibly_t<0> >::value, void>::type*) const any_executor.hpp:1682
boost::asio::any_io_executor boost::asio::any_io_executor::prefer<boost::asio::execution::detail::blocking::possibly_t<0> >(boost::asio::execution::detail::blocking::possibly_t<0> const&, boost::asio::constraint<traits::prefer_member<boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > const&, boost::asio::execution::detail::blocking::possibly_t<0> const&>::is_valid, int>::type) const any_io_executor.hpp:228
std::__1::enable_if<(call_traits<asio_prefer_fn::impl, boost::asio::any_io_executor&, void (boost::asio::execution::detail::blocking::possibly_t<0> const&)>::overload) == ((asio_prefer_fn::overload_type)3), asio_prefer_fn::call_traits<asio_prefer_fn::impl, boost::asio::any_io_executor&, void (boost::asio::execution::detail::blocking::possibly_t<0> const&), void, void, void, void, void, void, void>::result_type>::type asio_prefer_fn::impl::operator()<boost::asio::any_io_executor&, boost::asio::execution::detail::blocking::possibly_t<0> const&>(boost::asio::any_io_executor&, boost::asio::execution::detail::blocking::possibly_t<0> const&) const prefer.hpp:496
void boost::asio::detail::handler_work_base<boost::asio::any_io_executor, void, boost::asio::io_context, boost::asio::executor, void>::dispatch<boost::asio::detail::binder1<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::system::error_code>, CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> > >(boost::asio::detail::binder1<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::system::error_code>&, CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >&) handler_work.hpp:430
void boost::asio::detail::handler_work<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::asio::any_io_executor, void>::complete<boost::asio::detail::binder1<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::system::error_code> >(boost::asio::detail::binder1<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::system::error_code>&, CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >&) handler_work.hpp:505
boost::asio::detail::wait_handler<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::asio::any_io_executor>::do_complete(void*, boost::asio::detail::scheduler_operation*, boost::system::error_code const&, unsigned long) wait_handler.hpp:76
boost::asio::detail::scheduler_operation::complete(void*, boost::system::error_code const&, unsigned long) scheduler_operation.hpp:40
boost::asio::detail::scheduler::do_run_one(boost::asio::detail::conditionally_enabled_mutex::scoped_lock&, boost::asio::detail::scheduler_thread_info&, boost::system::error_code const&) scheduler.ipp:486
boost::asio::detail::scheduler::run(boost::system::error_code&) scheduler.ipp:204
boost::asio::io_context::run() io_context.ipp:63
main::$_1::operator()() const strand_demo.cpp:190
decltype(std::__1::forward<main::$_1>(fp)()) std::__1::__invoke<main::$_1>(main::$_1&&) type_traits:3747
void std::__1::__async_func<main::$_1>::__execute<>(std::__1::__tuple_indices<>) future:2186
std::__1::__async_func<main::$_1>::operator()() future:2179
std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::__execute() future:997
decltype(*(std::__1::forward<std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*>(fp0)).*fp()) std::__1::__invoke<void (std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::*)(), std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*, void>(void (std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::*&&)(), std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*&&) type_traits:3688
void std::__1::__thread_execute<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::*)(), std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*, 2ul>(std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::*)(), std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*>&, std::__1::__tuple_indices<2ul>) thread:280
void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::*)(), std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*> >(void*) thread:291
_pthread_start 0x00007fff204438fc
thread_start 0x00007fff2043f443

代码:

#include <chrono>
#include <iostream>
#include <utility>

#include "boost/asio.hpp"
#include <boost/pool/pool.hpp>
#include <boost/make_shared.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/bind/bind.hpp>

namespace details {

template <size_t SIZE, size_t... REST>
class Bucket;

template <size_t SIZE>
class Bucket<SIZE> {

public:
  Bucket() = default;

protected:
  void* do_alloc(const std::size_t numElements) {
    assert(numElements == 1);
    return allocator_.malloc();
  }

  void do_dealloc(void* const ptr, const std::size_t numElements) {
    assert(numElements == 1);
    allocator_.free(ptr, numElements);
  }

private:
  boost::pool<boost::default_user_allocator_new_delete> allocator_{SIZE};
};

template <size_t SIZE, size_t... REST>
class Bucket
  : public Bucket<SIZE>
  , public Bucket<REST>... {};

}  // namespace details

template <size_t SIZE, size_t... REST>
class BucketPool : public details::Bucket<SIZE, REST...> {
public:
  template <size_t S>
  void* alloc(const std::size_t numElements) {
    return details::Bucket<S>::do_alloc(numElements);
  }

  template <size_t S>
  void dealloc(void* const ptr, const std::size_t numElements) {
    assert(numElements == 1);
    details::Bucket<S>::do_dealloc(ptr, numElements);
  }
};


using strand_t = boost::asio::strand<boost::asio::io_context::executor_type>;

template <typename T, typename PoolType>
class ObjectAllocator {
public:
  using value_type = T;

  explicit ObjectAllocator(PoolType& bucketPool) : bucketPool_(bucketPool) {}

  template <typename U, typename K>
  explicit ObjectAllocator(const ObjectAllocator<U, K>& other)
  : bucketPool_(other.bucketPool_) {}

  bool operator==(const ObjectAllocator& lhs) const noexcept {
    return bucketPool_ == lhs.bucketPool_;
  }

  bool operator!=(const ObjectAllocator& lhs) const noexcept {
    return bucketPool_ != lhs.bucketPool_;
  }

  T* allocate(const std::size_t numElements) const {
    return static_cast<T*>(bucketPool_.template alloc<sizeof(T)>(numElements));
  }

  void deallocate(T* const ptr, const std::size_t numElements) const {
    bucketPool_.template dealloc<sizeof(T)>(ptr, numElements);
  }

private:
  template <typename, typename>
  friend class ObjectAllocator;

  PoolType& bucketPool_;
};

template <typename HandlerT>
class AsioTimer {

  class HandlerWrapper : public boost::enable_shared_from_this<HandlerWrapper> {
  public:
    HandlerWrapper(strand_t strand, HandlerT handler)
    : timer_(strand), handler_(handler), milliseconds_(0) {}

    void startTimer(const std::chrono::milliseconds& everyMilliseconds) {
      milliseconds_ = everyMilliseconds;
      timer_.expires_from_now(everyMilliseconds);
      startAsyncWait();
    }

  private:
    void startAsyncWait() {
      timer_.async_wait(MakeCustomAllocationHandler(
        memory_,
        boost::bind(&HandlerWrapper::handleTimerCallback, this->shared_from_this(),
                    boost::asio::placeholders::error)));
    }

    void handleTimerCallback(const boost::system::error_code& e) {
      if (e != boost::asio::error::operation_aborted) {
        handler_();
      }
      timer_.expires_at(timer_.expires_at() + milliseconds_);
      startAsyncWait();
    }

    BucketPool<128> memory_;
    boost::asio::steady_timer timer_;
    HandlerT handler_;
    std::chrono::milliseconds milliseconds_;
  };

public:
  AsioTimer(strand_t strand, HandlerT handler)
  : handlerWrapper_(boost::make_shared<HandlerWrapper>(strand, handler)) {}

  void startTimer(const std::chrono::milliseconds& everyMilliseconds) {
    handlerWrapper_->startTimer(everyMilliseconds);
  }

private:
  boost::shared_ptr<HandlerWrapper> handlerWrapper_;
};

template <typename HandlerT, typename PoolT>
class CustomAllocationHandler {
public:
  using allocator_type = ObjectAllocator<HandlerT, PoolT>;

  CustomAllocationHandler(PoolT& memory, HandlerT handler)
  : memory_(memory), handler_(std::move(handler)) {}

  allocator_type get_allocator() const noexcept {
    return allocator_type(memory_);
  }

  template <typename... Args>
  void operator()(Args&&... args) {
    handler_(std::forward<Args>(args)...);
  }

private:
  PoolT& memory_;
  HandlerT handler_;
};

template <typename HandlerT, typename PoolT>
CustomAllocationHandler<HandlerT, PoolT> MakeCustomAllocationHandler(PoolT& memory,
                                                                     HandlerT handler) {
  return CustomAllocationHandler<HandlerT, PoolT>(memory, std::move(handler));
}


int main() {
  boost::asio::io_context ioContext;

  strand_t myStrand(make_strand(ioContext));

  AsioTimer timer(myStrand, [] { std::cout << "timer called" << std::endl; });
  timer.startTimer(std::chrono::milliseconds(20));

  auto fut = std::async([&ioContext] {
    ioContext.run();
  });

  std::this_thread::sleep_for(std::chrono::seconds(1));
  ioContext.stop();
  fut.get();
}

有什么方法可以避免在调用 ioContext::run() 时调用 new

您的代码正在使用类型擦除的执行程序。它有效地运行在 asio::basic_stream_socket<asio::ip::tcp, asio::any_io_executor>,这会导致开销(分配和引用计数)。

要删除涉及的分配,请静态键入您的执行程序。请记住,它会使您的代码不那么灵活,因此如果您想将它与不同的执行程序一起使用,您可能需要在实际执行程序类型上进行模板化(例如 thread_pool vs io_context vs strand<>)。

例如

using executor_t = boost::asio::io_context::executor_type;
using socket_t   = boost::asio::basic_stream_socket<tcp, executor_t>;
using acceptor_t = boost::asio::basic_socket_acceptor<tcp, executor_t>;

请注意,这在实践中通常会大大提高 Asio 网络的性能!

Live Demo

为了好玩,我让它在回显之前反转了文本。输出“dlrow olleH”

#include <array>
#include <boost/asio.hpp>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <type_traits>
#include <utility>

using boost::asio::ip::tcp;

// Class to manage the memory to be used for handler-based custom allocation.
// It contains a single block of memory which may be returned for allocation
// requests. If the memory is in use when an allocation request is made, the
// allocator delegates allocation to the global heap.
class handler_memory {
  public:
    handler_memory() : in_use_(false)
    {
    }

    handler_memory(const handler_memory&) = delete;
    handler_memory& operator=(const handler_memory&) = delete;

    void* allocate(std::size_t size)
    {
        if (!in_use_ && size < sizeof(storage_)) {
            in_use_ = true;
            return &storage_;
        } else {
            return ::operator new(size);
        }
    }

    void deallocate(void* pointer)
    {
        if (pointer == &storage_) {
            in_use_ = false;
        } else {
            ::operator delete(pointer);
        }
    }

  private:
    // Storage space used for handler-based custom memory allocation.
    typename std::aligned_storage<1024>::type storage_;

    // Whether the handler-based custom allocation storage has been used.
    bool in_use_;
};

// The allocator to be associated with the handler objects. This allocator only
// needs to satisfy the C++11 minimal allocator requirements.
template <typename T> class handler_allocator {
  public:
    using value_type = T;

    explicit handler_allocator(handler_memory& mem) : memory_(mem)
    {
    }

    template <typename U>
    handler_allocator(const handler_allocator<U>& other) noexcept
        : memory_(other.memory_)
    {
    }

    bool operator==(const handler_allocator& other) const noexcept
    {
        return &memory_ == &other.memory_;
    }

    bool operator!=(const handler_allocator& other) const noexcept
    {
        return &memory_ != &other.memory_;
    }

    T* allocate(std::size_t n) const
    {
        return static_cast<T*>(memory_.allocate(sizeof(T) * n));
    }

    void deallocate(T* p, std::size_t /*n*/) const
    {
        return memory_.deallocate(p);
    }

  private:
    template <typename> friend class handler_allocator;

    // The underlying memory.
    handler_memory& memory_;
};

// Wrapper class template for handler objects to allow handler memory
// allocation to be customised. The allocator_type type and get_allocator()
// member function are used by the asynchronous operations to obtain the
// allocator. Calls to operator() are forwarded to the encapsulated handler.
template <typename Handler> class custom_alloc_handler {
  public:
    using allocator_type = handler_allocator<Handler>;

    custom_alloc_handler(handler_memory& m, Handler h) : memory_(m), handler_(h)
    {
    }

    allocator_type get_allocator() const noexcept
    {
        return allocator_type(memory_);
    }

    template <typename... Args> void operator()(Args&&... args)
    {
        handler_(std::forward<Args>(args)...);
    }

  private:
    handler_memory& memory_;
    Handler         handler_;
};

// Helper function to wrap a handler object to add custom allocation.
template <typename Handler>
inline custom_alloc_handler<Handler>
make_custom_alloc_handler(handler_memory& m, Handler h)
{
  return custom_alloc_handler<Handler>(m, h);
}

using executor_t = boost::asio::io_context::executor_type;
using socket_t   = boost::asio::basic_stream_socket<tcp, executor_t>;
using acceptor_t = boost::asio::basic_socket_acceptor<tcp, executor_t>;

class session : public std::enable_shared_from_this<session> {
  public:
    session(socket_t socket) : socket_(std::move(socket))
    {
    }

    void start()
    {
        do_read();
    }

  private:
    void do_read()
    {
        auto self(shared_from_this());
        socket_.async_read_some(
            boost::asio::buffer(data_),
            make_custom_alloc_handler(
                handler_memory_,
                [this, self](boost::system::error_code ec, std::size_t length) {
                    if (!ec) {
                        do_write(length);
                    }
                }));
    }

    void do_write(std::size_t length)
    {
        auto self(shared_from_this());

        if (length > 0) { // just for fun
            std::reverse(data_.begin(), data_.begin() + length - 1);
        }

        boost::asio::async_write(
            socket_, boost::asio::buffer(data_, length),
            make_custom_alloc_handler(handler_memory_,
                                      [this, self](boost::system::error_code ec,
                                                   std::size_t /*length*/) {
                                          if (!ec) {
                                              do_read();
                                          }
                                      }));
    }

    // The socket used to communicate with the client.
    socket_t socket_;

    // Buffer used to store data received from the client.
    std::array<char, 1024> data_;

    // The memory to use for handler-based custom memory allocation.
    handler_memory handler_memory_;
};

class server {
  public:
    server(boost::asio::io_context& io_context, short port)
        : acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
    {
        do_accept();
    }

  private:
    void do_accept()
    {
        acceptor_.async_accept(
            [this](boost::system::error_code ec, socket_t socket) {
                if (!ec) {
                    std::make_shared<session>(std::move(socket))->start();
                }

                do_accept();
            });
    }

    acceptor_t acceptor_;
};

int main(int argc, char* argv[])
{
    try {
        if (argc != 2) {
            std::cerr << "Usage: server <port>\n";
            return 1;
        }

        boost::asio::io_context io_context;
        server                  s(io_context, std::atoi(argv[1]));
        using namespace std::chrono_literals;
        io_context.run_for(3s);
        return 0;
    } catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << "\n";
        return 2;
    }
}

奖金

使用 C++17 CTAD,只需很少的更改即可在执行程序上对整个服务器进行模板化。

另请注意,它按值传递执行程序,更多地将执行上下文与服务对象分离。

Live On Coliru

#include <array>
#include <boost/asio.hpp>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <type_traits>
#include <utility>

using boost::asio::ip::tcp;

// Class to manage the memory to be used for handler-based custom allocation.
// It contains a single block of memory which may be returned for allocation
// requests. If the memory is in use when an allocation request is made, the
// allocator delegates allocation to the global heap.
class handler_memory {
  public:
    handler_memory() : in_use_(false)
    {
    }

    handler_memory(const handler_memory&) = delete;
    handler_memory& operator=(const handler_memory&) = delete;

    void* allocate(std::size_t size)
    {
        if (!in_use_ && size < sizeof(storage_)) {
            in_use_ = true;
            return &storage_;
        } else {
            return ::operator new(size);
        }
    }

    void deallocate(void* pointer)
    {
        if (pointer == &storage_) {
            in_use_ = false;
        } else {
            ::operator delete(pointer);
        }
    }

  private:
    // Storage space used for handler-based custom memory allocation.
    typename std::aligned_storage<1024>::type storage_;

    // Whether the handler-based custom allocation storage has been used.
    bool in_use_;
};

// The allocator to be associated with the handler objects. This allocator only
// needs to satisfy the C++11 minimal allocator requirements.
template <typename T> class handler_allocator {
  public:
    using value_type = T;

    explicit handler_allocator(handler_memory& mem) : memory_(mem)
    {
    }

    template <typename U>
    handler_allocator(const handler_allocator<U>& other) noexcept
        : memory_(other.memory_)
    {
    }

    bool operator==(const handler_allocator& other) const noexcept
    {
        return &memory_ == &other.memory_;
    }

    bool operator!=(const handler_allocator& other) const noexcept
    {
        return &memory_ != &other.memory_;
    }

    T* allocate(std::size_t n) const
    {
        return static_cast<T*>(memory_.allocate(sizeof(T) * n));
    }

    void deallocate(T* p, std::size_t /*n*/) const
    {
        return memory_.deallocate(p);
    }

  private:
    template <typename> friend class handler_allocator;

    // The underlying memory.
    handler_memory& memory_;
};

// Wrapper class template for handler objects to allow handler memory
// allocation to be customised. The allocator_type type and get_allocator()
// member function are used by the asynchronous operations to obtain the
// allocator. Calls to operator() are forwarded to the encapsulated handler.
template <typename Handler> class custom_alloc_handler {
  public:
    using allocator_type = handler_allocator<Handler>;

    custom_alloc_handler(handler_memory& m, Handler h) : memory_(m), handler_(h)
    {
    }

    allocator_type get_allocator() const noexcept
    {
        return allocator_type(memory_);
    }

    template <typename... Args> void operator()(Args&&... args)
    {
        handler_(std::forward<Args>(args)...);
    }

  private:
    handler_memory& memory_;
    Handler         handler_;
};

// Helper function to wrap a handler object to add custom allocation.
template <typename Handler>
inline custom_alloc_handler<Handler>
make_custom_alloc_handler(handler_memory& m, Handler h)
{
  return custom_alloc_handler<Handler>(m, h);
}


template <typename Executor>
class server {
    using executor_t = Executor;
    using socket_t   = boost::asio::basic_stream_socket<tcp, executor_t>;
    using acceptor_t = boost::asio::basic_socket_acceptor<tcp, executor_t>;

    class session : public std::enable_shared_from_this<session> {
      public:
        session(socket_t socket) : socket_(std::move(socket))
        {
        }

        void start()
        {
            do_read();
        }

      private:
        void do_read()
        {
            auto self(this->shared_from_this());
            socket_.async_read_some(
                boost::asio::buffer(data_),
                make_custom_alloc_handler(
                    handler_memory_,
                    [this, self](boost::system::error_code ec,
                                 std::size_t               length) {
                        if (!ec) {
                            do_write(length);
                        }
                    }));
        }

        void do_write(std::size_t length)
        {
            auto self(this->shared_from_this());

            if (length > 0) { // just for fun
                std::reverse(data_.begin(), data_.begin() + length - 1);
            }

            boost::asio::async_write(
                socket_, boost::asio::buffer(data_, length),
                make_custom_alloc_handler(
                    handler_memory_,
                    [this, self](boost::system::error_code ec,
                                 std::size_t /*length*/) {
                        if (!ec) {
                            do_read();
                        }
                    }));
        }

        // The socket used to communicate with the client.
        socket_t socket_;

        // Buffer used to store data received from the client.
        std::array<char, 1024> data_;

        // The memory to use for handler-based custom memory allocation.
        handler_memory handler_memory_;
    };

  public:
    server(Executor executor, short port)
        : acceptor_(executor, tcp::endpoint(tcp::v4(), port))
    {
        do_accept();
    }

  private:
    void do_accept()
    {
        acceptor_.async_accept(
            [this](boost::system::error_code ec, socket_t socket) {
                if (!ec) {
                    std::make_shared<session>(std::move(socket))->start();
                }

                do_accept();
            });
    }

    acceptor_t acceptor_;
};

int main(int argc, char* argv[])
{
    try {
        if (argc != 2) {
            std::cerr << "Usage: server <port>\n";
            return 1;
        }

        boost::asio::io_context io_context;
        server s(io_context.get_executor(), std::atoi(argv[1]));

        using namespace std::chrono_literals;
        io_context.run_for(3s);
        return 0;
    } catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << "\n";
        return 2;
    }
}