LINUX.ORG.RU

Архитектура boost::asio

 


0

1

Привет. Что-то я не совсем до конца понял как что за ливер в асио и как он функционирует. Наткнулся на такое и не понял зачем в аксептор отдают стрэнд. Чисто инуитивно - видимо альтернативная форма вместо той, когда стрэнд предается через async_…().

tcp::acceptor acceptor_;
net::io_context& ioc_;
...
acceptor_(net::make_strand(ioc))

В терминах асио, strand - executor. Разжуйте в целом механизм - как происходит вызов конечной функции-обработчика в асио и какую функцию в этом играет экзекъютер. Как бы понятно про проактор и все такое, поставили в очередь, задание запустилось на другом потоке (зашедшем в ран()), поток закончил, вызвали функтор обработчик события. Интересно именно роль в этом процессе executor’а, хочется понять принцип синхронизации через strand. Например, экзекъютер - контейнер асинхронных заданий с обработчиками, который выдает задание из своей очереди по запросу. Тут реализованы различные стратегии вроде strand (предыдущий не закончил, следущее задание strand не отдаст).

И еще не понял такое из асио доков

In the case of composed asynchronous operations, such as async_read() or async_read_until(), if a completion handler goes through a strand, then all intermediate handlers should also go through the same strand. 

Что за промежуточные хендлеры? Под хендлером ведь понимаются обработчики, которые передал в read_async(), например, но видимо я что-то не понял. Или промежуточный хендлер - это само чтение из сокета?

PS: конструктор аксептора в доках видел, но сильно яснее не становится:

The I/O executor that the acceptor will use, by default, to dispatch handlers for any asynchronous operations performed on the acceptor. 

PPS: нет бы в доках нарисовать достаточно подробную схему взаимодействия основных сущностей вместо тысячи слов, иначе идти в отладчик и штудировать backtrace, весело.

Не, strand парамеризируется типом executor’а, который вызывается для очереди, которая хранится в strand. Пойду копать под отладчиком

acceptor_(net::make_strand(ioc))

ибо clangd не справляется, похоже. И прыгает на конструктор, которые принимет экзекьютер.

pavlick ★★ ()
Ответ на: комментарий от anonymous

Да со strand все понятно, был затык с передачей его в аксептор (там в соответствующем бустовом заголовке все шаблонами обмазано, думал clangd брешит, нет). Оказалось, что он кастится к экзекютеру. Как весь этот зоопарк с экзекьютерами работает - мне пока не очень ясно.

pavlick ★★ ()
Ответ на: комментарий от Beewek

Благодарю за ссылки. Почитал немного

To address these temporary challenges and build toward the future, C++ must lay a foundation for controlling program execution. First, C++ must provide flexible facilities to control where and when work happens.

Тут конечно без бутылки не разобраться, экзекьютеры ведь не могут контролировать обработку переданных в async_…() хендлеров (в run() свои потоки заходят), это что получается - экзекьютерами можно выбрать, кто будет читать из сокета (грубо - цпу или гпу)?

pavlick ★★ ()
Ответ на: комментарий от Binkledum

Да, как сказали уже - это кусок кода из класса

class listener : public std::enable_shared_from_this<listener>
{
    net::io_context& ioc_;
    tcp::acceptor acceptor_;
    std::shared_ptr<std::string const> doc_root_;

public:
    listener(
        net::io_context& ioc,
        tcp::endpoint endpoint,
        std::shared_ptr<std::string const> const& doc_root)
        : ioc_(ioc)
        , acceptor_(net::make_strand(ioc))
        , doc_root_(doc_root)
    {

pavlick ★★ ()
Ответ на: комментарий от anonymous

Да к самому стренду вопросов нет. Давай про экзекьютеры, исполнение чего они контролируют? Там ведь идея с экзекьютерами такая, что выбирать вплоть до выполнения кода на ГПУ или ЦПУ. Зачем мне тогда заводить потоки в run(), если я могу выбрать выполнение на ГПУ? Значить исполнение финальных хендлеров - это не через экзекьютеры, они лишь выполняют нижележащие операции - чтение/запись в сокет, а финальный хендлер берет на себя поток из run(). Что думаешь?

pavlick ★★ ()
Ответ на: комментарий от anonymous

Ну экзекьютеры в стандарт тащат, это не чисто асио тема. Я тут спорить не буду. Только одно объясните - зачем в асио нужен экзекьютер? Что значит выполнить задание в разном месте и в разное время? Что за разность такая? Я завел потоки в ран(), мне нужно чтобы они выполняли все сразу, никакого разного мне врод не надо. Может я просто темный, о какой-то плюшке не знаю.

pavlick ★★ ()

Чего-то какое-то «хочу все знать», попробуй обратиться именно на фокусирующиеся ресурсы относительно своих нескончаемых почемучек, или читай документацию, а то ты в день по паре тем точно выдаешь.

anonymous ()
Ответ на: комментарий от pavlick

А вроде поняла, что это нужно для сериализации хандлеров завершения, работающих с разделяемым ресурсом использования без примитивов синхронизации.

anonymous ()
Ответ на: комментарий от pavlick

экзекьютеры ведь не могут контролировать обработку переданных в async_…() хендлеров

Экзекутор - это некий (лёгкий) объёкт, зная который, можно добраться до контекста (io_context или скажем thread_pool). Типа хэндла.

Ты создаёшь сокет (акцептор, …), передаёшь в конструктор этот экзекутор. Он там запоминается. Затем, когда ты вызываешь async_read(), чтение выполняется в этом контексте. Когда чтение завершается, то обработчик вызывается в этом же контексте. Также есть возможность передать вместе с обработчиком отдельный executor для вызова обработчика в контексте этого executor-а:

  async_getline(io_pool.executor(), std::cin,
      bind_executor(completion_pool.executor(),
        [](std::string line)
        {
          std::cout << "Line: " << line << "\n";
        }));

(см. примеры в src/examples/cpp14/executors)

Тут конечно без бутылки не разобраться

Да жуть вообще. Наворотили шаблонов десять этажей, никакое IDE нормально не покажет, а если и покажет, то там будет десятиэтажный нечитаемый шаблон.

Beewek ()
Ответ на: комментарий от pavlick

как спрашиваете так и отвечаю, у вас закрученные вопросы, типа - как пойти туда не знаю куда и принести то не знаю что ? не знаете ? проходите мимо

anonymous ()
Ответ на: комментарий от pavlick

Дело не в том знаю я или нет, а в том, что ты кажется ничего не знаешь, по количеству генерируемых тем, или не хочешь разбираться и ждешь что тебе все разжуют и в рот положат. Никакого самоуважения.

anonymous ()
Ответ на: комментарий от anonymous

Слушай, друг, я не домой к тебе пришел, мне плевать чего ты там думаешь. Не нравится - выход знаешь где, не флуди, пожалуйста. А притензии здесь лишь от администрации уместны.

pavlick ★★ ()
Ответ на: комментарий от Beewek

Все въехал, благодарю за ссылку на пример. Я ошибочно полагал, что все через ран() идет, а оно вон как можно. Не догадался, что там примеры есть.

ЗЫ: благодарю тех, кто хотел помочь разобраться.

pavlick ★★ ()
Последнее исправление: pavlick (всего исправлений: 1)
Ответ на: комментарий от anonymous

А если мои темы действительно настолько пусты и неуместны, то после 5 неанонимных комментов здесь в поддержку данного утверждения - удалю себя и больше не побеспокою сообщество впредь.

pavlick ★★ ()

Простым языком без пафосных терминов: при том же async_accept() само асинхронное accept инициируется в месте вызова, а его ожидание (epoll или что там еще на конкретной платформе) реально происходит вообще в worker thread, создаваемом один для всех сокетов асио. А io_service с его run() выступает в роли тупого цикла обработки событий, в который через post посылается событие завершения того же async_accept(). Strand - костыль для случая, когда io_service::run() юзается в более чем в одном треде одновременно, что обычно есть признак говнодизайна и что нужно исправлять на уровне ДНК, а не понимать.

Pavval ★★★★★ ()
Ответ на: комментарий от pavlick

Много чести тебя прогонять отсюда, пиши дальше если просто разобраться в вопросе тебе сложно самому, я лишь сказал как это выглядит со стороны, особенно когда кто-то, допустим ты, начинает пачками создавать темы, делай выводы сам.

anonymous ()