В замешательстве, когда тип 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_receiveReadHandler. Еще раз,обработчики вызываются только в потоках под управлением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_*методы.