【问题标题】:How to get boost::asio::io_service current action number如何获取 boost::asio::io_service 当前操作号
【发布时间】:2015-10-01 08:08:18
【问题描述】:

Boost::asio::io_service 为调试目的提供“处理程序跟踪”,它通过定义 BOOST_ASIO_ENABLE_HANDLER_TRACKING 启用,但将其数据记录到 stderr。我想在我的应用程序中使用此跟踪信息。我的问题是在我的应用程序中访问<action> 的最佳方式是什么?

关于为什么我想这样做的更多背景信息;我想将 <action> 作为参数附加到其他异步操作,以便跟踪原始请求的来源。

【问题讨论】:

  • 即使这是可能的,我想这也会导致性能下降。为什么不能使用从异步操作传递到异步操作的自己的标识符?
  • 我和 m.s.甚至可能包含一个活动 ID 作为 GUID,以便您可以跨服务器/客户端跟踪相关活动
  • 我的第一个选择是自己动手,但感觉就像一个肮脏的黑客。看到 boost 已经完成了大部分工作,我认为一些 boost 大师会有一些深奥的解决方案。
  • 这种“黑客”吸引人的一个原因是它允许对调用图进行快速“n”调试 - 无需修改每个调用站点并将此信息发送到 cerr 流以外的其他地方(例如记录器已经是应用程序的一部分)会很方便。

标签: c++ boost-asio


【解决方案1】:

Asio 不会公开其处理程序跟踪数据。尝试提取 Asio 中包含的跟踪信息比滚动自己的自定义处理程序更像是一种肮脏的黑客攻击。

这是来自 Asio 的 handler tracking 的 sn-p:

namespace boost {
namespace asio {
namespace detail {

class handler_tracking
{
public:
  class completion;

  // Base class for objects containing tracked handlers.
  class tracked_handler
  {
  private:
    // Only the handler_tracking class will have access to the id.
    friend class handler_tracking;
    friend class completion;
    uint64_t id_;

  // ...

  private:
    friend class handler_tracking;
    uint64_t id_;
    bool invoked_;
    completion* next_;
  };

// ...

private:
  struct tracking_state;
  static tracking_state* get_state();
};

} // namespace detail
} // namespace asio
} // namespace boost

正如其他人所提到的,在整个处理程序中传递 GUID 将允许关联多个异步操作。实现此目的的一种非侵入式方法是创建自定义跟踪处理程序类型,该类型包装现有处理程序并管理跟踪数据。有关自定义处理程序的示例,请参阅 Boost.Asio Invocation 示例。

另外,请注意,如果使用自定义处理程序类型,则在编写处理程序时应该非常小心。特别是,自定义处理程序类型的调用挂钩 (asio_handler_invoke()) 可能需要考虑其他处理程序的上下文。例如,如果没有明确说明从strand::wrap() 返回的包装处理程序,那么它将阻止中间操作在组合操作的正确上下文中运行。为避免必须显式处理此问题,可以使用 strand::wrap() 包装自定义处理程序:

boost::asio::async_read(..., strand.wrap(tracker.wrap(&handle_read))); // Good.
boost::asio::async_read(..., tracker.wrap(strand.wrap(&handle_read))); // Bad.

【讨论】:

  • 棒极了,像往常一样。你会认为处理程序组合的透明度是库缺陷吗?我想我知道(尽管我认为我可以有理由不使用显式处理程序机制“解决”它)。这肯定是一个很难很好地记录下来的陷阱。
  • @sehe 谢谢。我不认为这是一个缺陷,但它是 API 和文档都可以改进的一个领域。虽然文档涵盖了所有细节,但我认为用户将从一个明确的示例中受益匪浅,而不是将其作为练习让用户将所有部分拼凑在一起。此外,我确实喜欢 Asio 开发版本 (change history) 中的 API 更改,因为它更容易绑定和获取处理程序的执行器上下文。
【解决方案2】:

模拟 asio 调试处理程序跟踪的示例。注意事项:

  1. 假设 ioService 仅从单个线程运行。我从不使用任何其他方式,所以我不确定需要改变什么来解决这个限制。
  2. std::cerr 的非线程安全访问 - 将这个问题作为练习来解决。

代码

#include <boost/asio.hpp>
#include <boost/atomic.hpp>

#include <iostream>

class HandlerTracking
{
public:

    HandlerTracking()
        :
        mCount(1)
    { }


    template <class Handler>
    class WrappedHandler
    {
    public:

        WrappedHandler(HandlerTracking& t, Handler h, std::uint64_t id) 
            : 
            mHandlerTracking(t), 
            mHandler(h),
            mId(id)
        { }

        WrappedHandler(const WrappedHandler& other)
            :
            mHandlerTracking(other.mHandlerTracking),
            mHandler(other.mHandler),
            mId(other.mId),
            mInvoked(other.mInvoked)
        {
            other.mInvoked = true;
        }

        ~WrappedHandler()
        {
            if (!mInvoked)
                std::cerr << '~' << mId << std::endl;
        }

        template <class... Args>
        void operator()(Args... args)
        {
            mHandlerTracking.mCurrHandler = mId;
            std::cerr << '>' << mId << std::endl;

            try
            {
                mInvoked = true;
                mHandler(args...); 
            }
            catch(...)
            {
                std::cerr << '!' << mId << std::endl;
                throw;
            }
            std::cerr << '<' << mId << std::endl;
        }

        const std::uint64_t id() { return mId; }

    private:

        HandlerTracking& mHandlerTracking;
        Handler mHandler;
        const std::uint64_t mId;
        mutable bool mInvoked = false;
    };

    template <class Handler>
    WrappedHandler<Handler> wrap(Handler handler) 
    {
        auto next = mCount.fetch_add(1);
        std::cerr << mCurrHandler << '*' << next << std::endl;
        return WrappedHandler<Handler>(*this, handler, next);
    }

    boost::atomic<std::uint64_t> mCount;
    std::uint64_t mCurrHandler = 0;           // Note: If ioService run on multiple threads we need a curr handler per thread
};


// Custom invokation hook for wrapped handlers
//template <typename Function, typename Handler>
//void asio_handler_invoke(Function f, HandlerTracking::WrappedHandler<Handler>* h)
//{
//    std::cerr << "Context: " << h << ", " << h->id() << ", " << f.id() << std::endl;
//    f();
//}


// Class to demonstrate callback with arguments
class MockSocket
{
public:

    MockSocket(boost::asio::io_service& ioService) : mIoService(ioService) {}

    template <class Handler>
    void async_read(Handler h)
    {
        mIoService.post([h]() mutable { h(42); }); // we always read 42 bytes
    }

private:
    boost::asio::io_service& mIoService;
};

int main(int argc, char* argv[])
{
    boost::asio::io_service ioService;
    HandlerTracking tracking;

    MockSocket socket(ioService);

    std::function<void()> f1 = [&]() { std::cout << "Handler1" << std::endl; };
    std::function<void()> f2 = [&]() { std::cout << "Handler2" << std::endl; ioService.post(tracking.wrap(f1)); };
    std::function<void()> f3 = [&]() { std::cout << "Handler3" << std::endl; ioService.post(tracking.wrap(f2)); };
    std::function<void()> f4 = [&]() { std::cout << "Handler4" << std::endl; ioService.post(tracking.wrap(f3)); };

    std::function<void(int)> s1 = [](int s) { std::cout << "Socket read " << s << " bytes" << std::endl; };

    socket.async_read(tracking.wrap(s1)); 

    ioService.post(tracking.wrap(f1));
    ioService.post(tracking.wrap(f2));
    ioService.post(tracking.wrap(f3));
    auto tmp = tracking.wrap(f4);  // example handler destroyed without invocation

    ioService.run();



    return 0;
}

输出

0*1
0*2
0*3
0*4
0*5
>1
Socket read 42 bytes
<1
>2
Handler1
<2
>3
Handler2
3*6
<3
>4
Handler3
4*7
<4
>6
Handler1
<6
>7
Handler2
7*8
<7
>8
Handler1
<8
~5

【讨论】:

  • 在处理上述答案时想到的一个问题:stackoverflow.com/q/32857101/424153
  • 干得好!只是要注意另外两个警告:它不处理中间处理程序,也不处理运行io_service 的后续调用。这是一个demo,其中包含两种跟踪方法的输出。
  • 我在生产中使用上述内容 - 当(如果?)我根据您的建议改进它时,我会更新答案。感谢您的提示!
猜你喜欢
  • 2013-08-07
  • 2015-04-22
  • 2012-05-18
  • 2017-08-11
  • 1970-01-01
  • 2011-06-16
  • 2017-09-17
  • 2012-11-26
  • 2011-12-18
相关资源
最近更新 更多