大概介绍
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或者其它值传入。