Boost.ASIO源码:concurrency_hint与相关宏的值分析

大概介绍

concurrency_hint本身只是一个整型数值,在Boost.ASIO里经常可以看到作为构造函数参数传给各种服务(明确下:execution_context::service的子类),指明这些服务有没有多线程或其它并发逻辑,非并行情况下甚至还能稍稍简化下函数的执行逻辑,如:

	if (more_handlers && !one_thread_)
          wakeup_event_.unlock_and_signal_one(lock);
        else
          lock.unlock();

这是scheduler::do_run_one方法中的逻辑,如果scheduler是运行在单线程上,则直接解锁执行后续逻辑,否则解锁外还要唤醒其它的线程。这个one_thread_就是通过concurrency_hint经过计算得到的。

concurrency_hint与相关宏

以刚刚那个例子开始,以下展示scheduler的构造函数,里面需要传入一个concurrency_hint值,一系列计算后得到one_thread_这个flag的值:

scheduler::scheduler(
    boost::asio::execution_context& ctx, int concurrency_hint)
  : boost::asio::detail::execution_context_service_base<scheduler>(ctx),

	//只关注这一坨
    one_thread_(concurrency_hint == 1
        || !BOOST_ASIO_CONCURRENCY_HINT_IS_LOCKING(
          SCHEDULER, concurrency_hint)
        || !BOOST_ASIO_CONCURRENCY_HINT_IS_LOCKING(
          REACTOR_IO, concurrency_hint)),

	// 这里其实也是一样的逻辑
    mutex_(BOOST_ASIO_CONCURRENCY_HINT_IS_LOCKING(
          SCHEDULER, concurrency_hint)),
    task_(0),
    task_interrupted_(true),
    outstanding_work_(0),
    stopped_(false),
    shutdown_(false),
    concurrency_hint_(concurrency_hint)
{
  BOOST_ASIO_HANDLER_TRACKING_INIT;
}

这时候再看相关的宏定义,以下宏均来自concurrency_hint.hpp:

// Helper macro to determine if locking is enabled for a given facility.
#define BOOST_ASIO_CONCURRENCY_HINT_IS_LOCKING(facility, hint) \
  ( ( ( static_cast<unsigned>(hint) \
    & ( BOOST_ASIO_CONCURRENCY_HINT_ID_MASK \
      | BOOST_ASIO_CONCURRENCY_HINT_LOCKING_ ## facility ) ) \
        ^ BOOST_ASIO_CONCURRENCY_HINT_ID ) != 0 )

// The concurrency hint ID and mask are used to identify when a "well-known"
// concurrency hint value has been passed to the io_context.
#define BOOST_ASIO_CONCURRENCY_HINT_ID 0xA5100000u
#define BOOST_ASIO_CONCURRENCY_HINT_ID_MASK 0xFFFF0000u

// If set, this bit indicates that the scheduler should perform locking.
#define BOOST_ASIO_CONCURRENCY_HINT_LOCKING_SCHEDULER 0x1u

// If set, this bit indicates that the reactor should perform locking when managing descriptor registrations.
#define BOOST_ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_REGISTRATION 0x2u

// If set, this bit indicates that the reactor should perform locking for I/O.
#define BOOST_ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_IO 0x4u

BOOST_ASIO_CONCURRENCY_HINT_IS_LOCKING ( facility, hint )中的hint便是外面传入的concurrency_hint值,facility并不是变量,它仅用作文本拼接,使得能通过facility一个值灵活选择调用其它的宏(不懂的可查 ## 在宏中的作用)。facility相关的那3个宏注释讲得很清楚这里就跳过,再看BOOST_ASIO_CONCURRENCY_HINT_ID和它的掩码,ID先把它认为是一个没啥语义信息的字节串,从它的掩码可以看出来,这个ID的有效位应该是高16位,低16位则交给facility相关宏来控制,而它们的宏的控制位正好分别在低一二三位上。

下面以传入的facility为REACTOR_IO为例解释BOOST_ASIO_CONCURRENCY_HINT_IS_LOCKING的计算逻辑:
计算的时候会先计算 BOOST_ASIO_CONCURRENCY_HINT_ID_MASK | BOOST_ASIO_CONCURRENCY_HINT_LOCKING_REACTOR_IO 得到的串为0xFFFF0004,这个值再跟hint进行&运算,先假设得到的值是A,这个值会再与那个毫无规律的ID最后进行或运算,最后返回最终结果是否为0的布尔值。将上述逻辑稍微简化一下,就是返回中间值A是否不等于ID(只有2个相等的值异或后才能得到0)。再换句话说,就相当于
return ( 0xFFFF0004 & hint ) != BOOST_ASIO_CONCURRENCY_HINT_ID
至此语义就很明显了——只有传入值hint等于ID时,这个宏才会返回false。

concurrency_hint值的语义分析

再回到最开始的构造函数上来,下面把关键部分重新截出来:

    one_thread_(concurrency_hint == 1
        || !BOOST_ASIO_CONCURRENCY_HINT_IS_LOCKING(
          SCHEDULER, concurrency_hint)
        || !BOOST_ASIO_CONCURRENCY_HINT_IS_LOCKING(
          REACTOR_IO, concurrency_hint)),

很明显可以看出,concurrency_hint为 1 时直接代表老子要用单线程了;不为1时,只有传concurrency_hint为BOOST_ASIO_CONCURRENCY_HINT_ID那两个宏函数才会返回false,也就代表老子铁了心要用单线程。其它情况都代表多线程。而根据那3个facility,也就是REACTOR_IO、SCHEDULER、REACTOR_REGISTRATION的宏定义,传入concurrency_hint为某个宏的的值的话就代表该facility要采用多线程,而不论concurrency_hint传入哪个宏的值,实际上3个facility的宏函数计算都会得到true,也就是三个都得用多线程,语义上就是一个要用多线程,那么你另外两个也得多线程。
而且推导出的语义与concurrency_hint.hpp下的另一条宏定义不谋而合:

// This special concurrency hint disables locking in both the scheduler and
// reactor I/O. This hint has the following restrictions:
#define BOOST_ASIO_CONCURRENCY_HINT_UNSAFE static_cast<int>(BOOST_ASIO_CONCURRENCY_HINT_ID)

BOOST_ASIO_CONCURRENCY_HINT_ID这个值就是代表不加锁,连名字都定义成unsafe了。

再小小延申一下,调用scheduler这个构造函数的地方传入的concurrency_hint情况:

//io_context.hpp
typedef detail::io_context_impl impl_type;
typedef class scheduler io_context_impl;

//io_context.ipp
io_context::io_context()
  : impl_(add_impl(new impl_type(*this, BOOST_ASIO_CONCURRENCY_HINT_DEFAULT))) {}

io_context::io_context(int concurrency_hint)
  : impl_(add_impl(new impl_type(*this, concurrency_hint == 1
          ? BOOST_ASIO_CONCURRENCY_HINT_1 : concurrency_hint))) {}
# define BOOST_ASIO_CONCURRENCY_HINT_DEFAULT -1
# define BOOST_ASIO_CONCURRENCY_HINT_1 1

由上可以看出默认情况下传入的concurrency_hint是-1(二进制补码为0xFFFFFFFF);当然也可以外界自己指定concurrency_hint为1或者其它值传入。

猜你喜欢

转载自blog.csdn.net/weixin_43827934/article/details/84590572