Зачем нам нужны async и io_context?

Я изучаю многопоточность и обнаружил, что async() предпочтительнее использовать с io_context. Но я не совсем понимаю, зачем они нужны. Я перечитал несколько документации и посмотрел много лекций на эту тему, но так и не смог понять, в чем разница, например, в этом коде:

boost::asio::io_context io;

io.post([](){ 
   do_some_work() 
});

std::thread thread([&io]() {
 io.run();
});

из этого:

std::thread thread([&io]() {
 do_some_work();
});

Я также видел много примеров использования io_context с async во взаимодействии клиент-сервер с так называемыми обработчиками. Но я просто не понимаю их смысла. Зачем они нужны, если можно просто писать методы подряд:

socket();
connect();
read();
handle();

Зачем использовать сложную конструкцию async с io_context, если можно написать всё структурно (как я сделал выше) с помощью future/promise и будет читабельно и понятно?

Потоки — довольно сложная вещь для создания, а отдельные операции, запланированные в этом потоке, обходятся дешевле по сравнению с накладными расходами на создание потока. Кроме того, асинхронные операции для нескольких цепочек выполнения могут выполняться не по порядку и с пакетными результатами. Есть также вещи, которые вы не можете сделать с синхронным API, например, настроить буферы приема настолько заранее, что сетевая карта может фактически использовать их почти напрямую в качестве цели, без необходимости пробуждения вашего процесса.

Ext3h 02.07.2024 06:40
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
87
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

При синхронных вызовах сокетов вы можете обрабатывать только одно соединение на поток. Как правило, отправка и получение данных по сети занимает гораздо больше времени, чем обработка данных, это приводит к тому, что поток проводит большую часть своего времени в режиме ожидания в ожидании операций. завершить. На сервере, обрабатывающем множество соединений, создание нового потока для каждого соединения может оказаться неэффективным, поскольку создание и обслуживание потоков обычно обходится относительно дорого.

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

io_context — это asio-реализация этого пула потоков. В вашем тривиальном примере да, его использование бесполезно, но более реалистичным примером будет:

boost::asio::io_context io;
void do_some_work(){
  // Do some asynchronous operation
  // Run the callback for it
  io.post(async_complete());
}
for (int i = 0; i< 10; i++)
{
  io.post([](){ 
     do_some_work() 
  });
}

std::thread thread([&io]() {
 io.run();
});
io_context asio-реализация пула потоков?! Я почти уверен, что boost::asio::thread_pool — это реализация пула потоков. Это во многом говорит об этом в названии. Фактически, весь смысл io.run() в том, что io_context нужен поток выполнения, чтобы действительно что-то сделать; у него нет своего.
MSalters 02.07.2024 09:30

@MSalters, ок, точнее, это рабочая очередь, которая затем может использоваться пулом потоков для выполнения задач, я больше пытался объяснить основные концепции асинхронного кода, а не технические детали asio

Alan Birtles 02.07.2024 10:25

Хорошо, но разве мы не можем написать то же самое, просто создав поток и зациклившись в нем? Вот так: std::thread thread([&io]() { for (int i = 0; i< 10; i++) { do_some_work() } async_complete() });

Danek 02.07.2024 11:35

Или основная цель использования io_context — выполнение операций в очереди?

Danek 02.07.2024 11:38

он предназначен для выполнения операций в очереди, ваш цикл будет выполнять всю работу, а затем вызывать обратный вызов, версия asio будет выполнять каждую часть работы (возможно, все одновременно), а затем вызывать обратный вызов для каждой части работы, и все это в одном потоке

Alan Birtles 02.07.2024 12:07

Чтобы ответить на последнюю часть вопроса: почему бы и нет future/promise ? Это потому, что это не сработает. Чтобы что-то сделать, вам понадобится std::async вместо std::promise. И это не имеет прямого контроля над потоками выполнения. std::launch::async требуются новые темы или, по крайней мере, темы, которые выглядят новыми (локальные значения потоков очищены)

Более логичное решение вашего шаблона

socket();
connect();
while(...) {
   read();
   handle();
}

было бы

co_await socket();
co_await connect();
while(...) {
    co_yield read();
}

(Использование сопрограмм C++20 - co_yield позволяет вызывающей стороне обрабатывать каждое возвращаемое значение)

Другие вопросы по теме