1. 程式人生 > >Boost.ASIO原始碼:concurrency_hint與相關巨集的值分析

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或者其它值傳入。