これらのアプローチは、明示的に相互に排他的ではありません。最初と 2 番目の組み合わせをよく見かけます。
- 1 つ以上のスレッドが 1 つの でネットワーク I/O を処理しています
io_service
。
- 長時間実行またはブロックしているタスクは、別の に投稿されます
io_service
。これio_service
は、ネットワーク I/O を処理するスレッドに干渉しないスレッド プールとして機能します。または、長時間実行またはブロックするタスクが必要になるたびに、切り離されたスレッドを生成することもできます。ただし、スレッドの作成/破棄のオーバーヘッドが顕著な影響を与える可能性があります。
スレッドプールの実装を提供するこの回答。さらに、これは 2 つの 間の相互作用を強調しようとする基本的な例ですio_services
。
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/chrono.hpp>
#include <boost/optional.hpp>
#include <boost/thread.hpp>
/// @brief Background service will function as a thread-pool where
/// long-standing blocking operations may occur without affecting
/// the network event loop.
boost::asio::io_service background_service;
/// @brief The main io_service will handle network operations.
boost::asio::io_service io_service;
boost::optional<boost::asio::io_service::work> work;
/// @brief ODBC blocking operation.
///
/// @brief data Data to use for query.
/// @brief handler Handler to invoke upon completion of operation.
template <typename Handler>
void query_odbc(unsigned int data,
Handler handler)
{
std::cout << "in background service, start querying odbc\n";
std::cout.flush();
// Mimic busy work.
boost::this_thread::sleep_for(boost::chrono::seconds(5));
std::cout << "in background service, posting odbc result to main service\n";
std::cout.flush();
io_service.post(boost::bind(handler, data * 2));
}
/// @brief Functions as a continuation for handle_read, that will be
/// invoked with results from ODBC.
void handle_read_odbc(unsigned int result)
{
std::stringstream stream;
stream << "in main service, got " << result << " from odbc.\n";
std::cout << stream.str();
std::cout.flush();
// Allow io_service to stop in this example.
work = boost::none;
}
/// @brief Mocked up read handler that will post work into a background
/// service.
void handle_read(const boost::system::error_code& error,
std::size_t bytes_transferred)
{
std::cout << "in main service, need to query odbc" << std::endl;
typedef void (*handler_type)(unsigned int);
background_service.post(boost::bind(&query_odbc<handler_type>,
21, // data
&handle_read_odbc) // handler
);
// Keep io_service event loop running in this example.
work = boost::in_place(boost::ref(io_service));
}
/// @brief Loop to show concurrency.
void print_loop(unsigned int iteration)
{
if (!iteration) return;
std::cout << " in main service, doing work.\n";
std::cout.flush();
boost::this_thread::sleep_for(boost::chrono::seconds(1));
io_service.post(boost::bind(&print_loop, --iteration));
}
int main()
{
boost::optional<boost::asio::io_service::work> background_work(
boost::in_place(boost::ref(background_service)));
// Dedicate 3 threads to performing long-standing blocking operations.
boost::thread_group background_threads;
for (std::size_t i = 0; i < 3; ++i)
background_threads.create_thread(
boost::bind(&boost::asio::io_service::run, &background_service));
// Post a mocked up 'handle read' handler into the main io_service.
io_service.post(boost::bind(&handle_read,
make_error_code(boost::system::errc::success), 0));
// Post a mockup loop into the io_service to show concurrency.
io_service.post(boost::bind(&print_loop, 5));
// Run the main io_service.
io_service.run();
// Cleanup background.
background_work = boost::none;
background_threads.join_all();
}
そして出力:
メイン サービスでは、odbc にクエリを実行する必要があります
主なサービスで、仕事をしています。
バックグラウンド サービスで、odbc のクエリを開始します
主なサービスで、仕事をしています。
主なサービスで、仕事をしています。
主なサービスで、仕事をしています。
主なサービスで、仕事をしています。
バックグラウンド サービスで、odbc の結果をメイン サービスにポストする
メインサービスでは、odbc から 42 を取得しました。
io_service
メインポストを処理する 1 つのスレッドがに作用し、 がブロックbackground_service
している間、そのイベント ループを処理し続けることに注意してください。background_service
がbackground_service
結果を取得すると、ハンドラーを main にポストしio_service
ます。