В замешательстве, когда тип boost::asio-интерфейса::Ио службу запустить метод блокирует/разблокирует
будучи полным новичком, чтобы повысить.Асио, меня путают с io_service::run()
. Я был бы признателен, если бы кто-то мог объяснить мне, когда этот метод блокирует/разблокирует. В документации говорится:
The
run()
функциональные блоки до тех пор, пока вся работа не будет завершена, и нет больше обработчиков, которые будут отправлены, или доio_service
была остановлена.несколько потоков могут вызвать
run()
функция для настройки пула потоков из что заio_service
может выполнять обработчики. Все потоки, ожидающие в пуле, эквивалентны иio_service
может выбрать любой из них для вызова обработчика.нормальный выход из означает, что
io_service
объект остановлен (thestopped()
функция возвращает значение true). Последующие звонки наrun()
,run_one()
,poll()
илиpoll_one()
вернется немедленно, если нет предварительного вызоваreset()
.
что делает следующее заявление значит?
[...] нет больше обработчиков, которые будут отправлены [...]
при попытке понять поведение io_service::run()
, я наткнулся на это пример (пример 3а). В нем я замечаю, что io_service->run()
блокирует и ждет рабочих заданий.
// WorkerThread invines io_service->run()
void WorkerThread(boost::shared_ptr<boost::asio::io_service> io_service);
void CalculateFib(size_t);
boost::shared_ptr<boost::asio::io_service> io_service(
new boost::asio::io_service);
boost::shared_ptr<boost::asio::io_service::work> work(
new boost::asio::io_service::work(*io_service));
// ...
boost::thread_group worker_threads;
for(int x = 0; x < 2; ++x)
{
worker_threads.create_thread(boost::bind(&WorkerThread, io_service));
}
io_service->post( boost::bind(CalculateFib, 3));
io_service->post( boost::bind(CalculateFib, 4));
io_service->post( boost::bind(CalculateFib, 5));
work.reset();
worker_threads.join_all();
однако в следующем коде, над которым я работал, клиент подключается с помощью TCP / IP, а метод run блокируется до тех пор, пока данные не будут асинхронно полученный.
typedef boost::asio::ip::tcp tcp;
boost::shared_ptr<boost::asio::io_service> io_service(
new boost::asio::io_service);
boost::shared_ptr<tcp::socket> socket(new tcp::socket(*io_service));
// Connect to 127.0.0.1:9100.
tcp::resolver resolver(*io_service);
tcp::resolver::query query("127.0.0.1",
boost::lexical_cast< std::string >(9100));
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
socket->connect(endpoint_iterator->endpoint());
// Just blocks here until a message is received.
socket->async_receive(boost::asio::buffer(buf_client, 3000), 0,
ClientReceiveEvent);
io_service->run();
// Write response.
boost::system::error_code ignored_error;
std::cout << "Sending message n";
boost::asio::write(*socket, boost::asio::buffer("some data"), ignored_error);
объяснения run()
это описывает его поведение в двух примерах ниже было бы оценено.
2 ответа:
Фонд
давайте начнем с упрощенного примера и рассмотрим соответствующий импульс.Асио штук:
void handle_async_receive(...) { ... } void print() { ... } ... boost::asio::io_service io_service; boost::asio::ip::tcp::socket socket(io_service); ... io_service.post(&print); // 1 socket.connect(endpoint); // 2 socket.async_receive(buffer, &handle_async_receive); // 3 io_service.post(&print); // 4 io_service.run(); // 5
Что Это проводник?
A проводник это не более чем вызов. В примере кода есть 3 обработчика:
- The
- The
handle_async_receive
обработчик (3).- The
хотя то же самое
print()
функция используется дважды, каждое использование считается создать свой собственный однозначно идентифицируемый обработчик. Обработчики могут иметь различные формы и размеры, начиная от базовых функций, подобных приведенным выше, и заканчивая более сложными конструкциями, такими как функторы, созданные изboost::bind()
и лямбды. Независимо от сложности, обработчик по-прежнему остается не более чем обратным вызовом.Что Это работа?
работа-это некоторая обработка, которая повышает.Асио был просил сделать от имени приложения код. Иногда Повышают.Asio может начать какую-то работу, как только ему об этом скажут, а в других случаях он может подождать, чтобы выполнить работу в более поздний момент времени. Как только он закончит работу, повысьте.Asio проинформирует приложение, вызвав прилагаемый проводник.
импульс.Asio гарантирует, что обработчики будет работать только в потоке, который в настоящее время вызывает
run()
,run_one()
,poll()
, илиpoll_one()
. Это потоки, которые будут работать и вызывать обработчики. Поэтому, в приведенном выше примере,print()
не вызывается, когда он размещен вio_service
(1). Вместо этого он добавляется вio_service
и будет вызываться в более поздний момент времени. В этом случае он в пределахio_service.run()
(5).Что Такое Асинхронные Операции?
An асинхронная операция создает работу и повысить.Асио будет вызывать проводник to сообщите заявке, когда работа будет завершена. Асинхронные операции создаются путем вызова функции, которая имеет имя с префиксом
async_
. Эти функции также известны как инициирование функции.асинхронные операции можно разложить на три шага:
- инициирование, или информирование, связанное с
io_service
это работает должно быть сделано. Элементasync_receive
операция (3) сообщаетio_service
что нужно будет асинхронно считывает данные из сокета, затемasync_receive
сразу возвращается.- выполнение фактической работы. В этом случае, когда
socket
получает данные, байты будут считаны и скопированы вbuffer
. Фактическая работа будет выполнена в любом:
- инициирующая функция (3), Если Boost.Асио может определить, что он не будет блокировать.
- когда приложение явно запустить
io_service
(5).- вызов
handle_async_receive
ReadHandler. Еще раз,обработчики вызываются только в потоках под управлениемio_service
. Таким образом, независимо от того, когда выполнена работа (3 или 5), гарантируется, чтоhandle_async_receive()
будет вызываться только в пределахio_service.run()
(5).разделение во времени и пространстве между этими тремя шагами известно как инверсия потока управления. Это одна из сложностей, которая затрудняет асинхронное программирование. Тем не менее, есть методы, которые могут помогите смягчить это, например, с помощью сопрограммы.
Что Значит
io_service.run()
делать?когда поток вызывает
io_service.run()
работа и обработчики будет вызван из этого потока. В приведенном выше примереio_service.run()
(5) будет блокировать до:
- он вызвал и вернулся из обоих
handle_async_receive
обработчик был вызван и возвращенный.- The
io_service
явно остановлен черезio_service::stop()
.- исключение из обработчика.
один потенциальный поток psuedo-ish можно описать следующим образом:
create io_service create socket add print handler to io_service (1) wait for socket to connect (2) add an asynchronous read work request to the io_service (3) add print handler to io_service (4) run the io_service (5) is there work or handlers? yes, there is 1 work and 2 handlers does socket have data? no, do nothing run print handler (1) is there work or handlers? yes, there is 1 work and 1 handler does socket have data? no, do nothing run print handler (4) is there work or handlers? yes, there is 1 work does socket have data? no, continue waiting -- socket receives data -- socket has data, read it into buffer add handle_async_receive handler to io_service is there work or handlers? yes, there is 1 handler run handle_async_receive handler (3) is there work or handlers? no, set io_service as stopped and returnобратите внимание, как когда чтение закончилось, он добавил еще один проводник до
io_service
. Эта тонкая деталь является важной особенностью асинхронного программирования. Это позволяет обработчики быть скованными вместе. Например, еслиhandle_async_receive
не получил все данные, которые он ожидал, то его реализация может опубликовать еще одну асинхронную операцию чтения, в результате чегоio_service
имея больше работы, и, таким образом, не возвращаясь изio_service.run()
.обратите внимание, что когда
io_service
закончилась работа, приложение должноreset()
theio_service
перед запуском его снова.
пример вопроса и пример 3A кода
теперь изучите два фрагмента кода, на которые ссылается вопрос.
Вопрос Код
socket->async_receive
добавляет работу вio_service
. Таким образом,io_service->run()
блокируется до тех пор, пока операция чтения не завершится с успехом или ошибкой, иClientReceiveEvent
либо закончил работу, либо создает исключение.пример 3a код
в надежде сделать его легче понять, вот меньший аннотированный пример 3a:
void CalculateFib(std::size_t n); int main() { boost::asio::io_service io_service; boost::optional<boost::asio::io_service::work> work = // '. 1 boost::in_place(boost::ref(io_service)); // .' boost::thread_group worker_threads; // -. for(int x = 0; x < 2; ++x) // : { // '. worker_threads.create_thread( // :- 2 boost::bind(&boost::asio::io_service::run, &io_service) // .' ); // : } // -' io_service.post(boost::bind(CalculateFib, 3)); // '. io_service.post(boost::bind(CalculateFib, 4)); // :- 3 io_service.post(boost::bind(CalculateFib, 5)); // .' work = boost::none; // 4 worker_threads.join_all(); // 5 }
в высокого уровня, программа создаст 2 темы, которые будут обрабатывать
io_service
'цикл событий (2). Это приводит к простому пулу потоков, который будет вычислять числа Фибоначчи (3).единственное существенное различие между кодом вопроса и этим кодом заключается в том, что этот код вызывает
io_service::run()
(2) до фактическая работа и обработчики добавляются вio_service
(3). Чтобы предотвратитьio_service::run()
сразу же возвращаясь, кio_service::work
объект создан (1). Этот объект предотвращаетio_service
из-за работы, поэтомуio_service::run()
не вернется в итоге никакой работы.общий поток выглядит следующим образом:
- создать и добавить
чтобы упростить как то, что
run
думает ли он, как сотрудник, который должен обрабатывать кучу бумаги; он берет один лист, делает то, что лист говорит, выбрасывает лист и берет следующий; когда у него заканчиваются листы, он покидает офис. На каждом листе может быть любая инструкция, даже добавление нового листа в стопку. Вернемся к asio: вы можете датьio_service
работаем в двух направлениях, по существу: с помощьюpost
на нем как в примере вы связаны, или с помощью других объектов, которые внутренний вызовpost
наio_service
, какsocket
иasync_*
методы.