В замешательстве, когда тип boost::asio-интерфейса::Ио службу запустить метод блокирует/разблокирует


будучи полным новичком, чтобы повысить.Асио, меня путают с io_service::run(). Я был бы признателен, если бы кто-то мог объяснить мне, когда этот метод блокирует/разблокирует. В документации говорится:

The run() функциональные блоки до тех пор, пока вся работа не будет завершена, и нет больше обработчиков, которые будут отправлены, или до io_service была остановлена.

несколько потоков могут вызвать run() функция для настройки пула потоков из что за io_service может выполнять обработчики. Все потоки, ожидающие в пуле, эквивалентны и io_service может выбрать любой из них для вызова обработчика.

нормальный выход из означает, что io_service объект остановлен (the stopped() функция возвращает значение 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 67

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 print обработчик (1).
  • The handle_async_receive обработчик (3).
  • The print обработчик (4).

хотя то же самое 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) будет блокировать до:

  • он вызвал и вернулся из обоих print обработчики, операция получения завершается с успехом или неудачей, и его 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() the io_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() не вернется в итоге никакой работы.

общий поток выглядит следующим образом:

  1. создать и добавить

чтобы упростить как то, что run думает ли он, как сотрудник, который должен обрабатывать кучу бумаги; он берет один лист, делает то, что лист говорит, выбрасывает лист и берет следующий; когда у него заканчиваются листы, он покидает офис. На каждом листе может быть любая инструкция, даже добавление нового листа в стопку. Вернемся к asio: вы можете датьio_service работаем в двух направлениях, по существу: с помощью post на нем как в примере вы связаны, или с помощью других объектов, которые внутренний вызов post на io_service, как socket и async_* методы.