Синхронная асинхронность в C++ | OTUS
⚡ Подписка на курсы OTUS!
Интенсивная прокачка навыков для IT-специалистов!
Подробнее

Курсы

Программирование
Алгоритмы и структуры данных Team Lead Архитектура и шаблоны проектирования Разработчик IoT C# Developer. Professional PostgreSQL Разработчик на Spring Framework
-5%
Flutter Mobile Developer NoSQL iOS Developer. Basic
-10%
C++ Developer. Basic C++ Developer. Professional Android Developer. Professional Microservice Architecture Unity Game Developer. Professional Базы данных Node.js Developer React.js Developer Специализация Java-разработчик
-25%
Web-разработчик на Python Framework Laravel Cloud Solution Architecture Vue.js разработчик Интенсив «Оптимизация в Java» Супер - интенсив по паттернам проектирования Супер - интенсив по Kubernetes Супер-интенсив "Tarantool" PHP Developer. Basic
Инфраструктура
Мониторинг и логирование: Zabbix, Prometheus, ELK Administrator Linux. Professional Дизайн сетей ЦОД Разработчик IoT PostgreSQL Экспресс-курс "Версионирование и командная работа с помощью Git"
-30%
Microservice Architecture Highload Architect Специализация Administrator Linux
-25%
Network engineer Cloud Solution Architecture Внедрение и работа в DevSecOps Супер-практикум по работе с протоколом BGP Супер - интенсив по паттернам проектирования Супер - интенсив по Kubernetes Супер-интенсив «СУБД в высоконагруженных системах» Супер-интенсив "Tarantool" Network engineer. Basic
Корпоративные курсы
Безопасность веб-приложений IT-Recruiter Дизайн сетей ЦОД Компьютерное зрение Разработчик IoT Вебинар CERTIPORT Machine Learning. Professional
-6%
NoSQL Пентест. Практика тестирования на проникновение Java QA Engineer. Базовый курс Руководитель поддержки пользователей в IT
-8%
SRE практики и инструменты Cloud Solution Architecture Внедрение и работа в DevSecOps Супер-практикум по работе с протоколом BGP Infrastructure as a code Супер-практикум по использованию и настройке GIT Промышленный ML на больших данных Экспресс-курс «CI/CD или Непрерывная поставка с Docker и Kubernetes» BPMN: Моделирование бизнес-процессов Основы Windows Server
Специализации Курсы в разработке Подготовительные курсы Подписка
+7 499 938-92-02

Синхронная асинхронность в C++

С___Deep_10-5020-8d2073.09_site.png

Наверняка все, кто изучал старый добрый стандарт C++11, знают о существовании в стандартной библиотеке вызова std::async, который позволяет выполнить некий код асинхронно (более точно – поведение указывается первым параметром вызова).

Согласно документации, вызов с параметром std::launch::async обещает выполнить пользовательский код в отдельном потоке. Посмотрим на приведённый ниже код.

    #include <future>
    #include <iostream>
    #include <thread>

     int main(int argc, char* argv[]) {
        int count = 10;

         std::async(std::launch::async, [&count] {
             for(int i=0; i<count; ++i) {
               std::cout << 1;
               std::this_thread::sleep_for(std::chrono::milliseconds(1));
           }
       });
       std::async(std::launch::async, [&count] {
           for(int i=0; i<count; ++i) {
               std::cout << 2;
               std::this_thread::sleep_for(std::chrono::milliseconds(1));
           }
       });

       return 0;
   }

В строках 8-13 запускаем асинхронное выполнение простой lambda-функции, которая должна вывести на экран цифру «1» каждую миллисекунду десять раз. В строках 14-19 запускаем выполнение аналогичной функции, но на этот раз она будет выводить на экран цифру «2». Что можно ожидать на экране по окончанию выполнения программы?

Кто сказал, что «результат не определён»?

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

12212121211212211221

Звучит логично, но эта гипотеза неверна. На самом деле на экран гарантированно будет выведена последовательность:

11111111112222222222

Почему? Что произошло?

А произошла принудительная синхронизация двух потоков. Выполнение второго потока (с выводом цифры «2») гарантированно начнётся только после того, как первый поток закончит своё выполнение.

Кто догадается, почему?

На самом деле не всё так просто. Но достаточно задуматься, про что мы забыли в этом примере? А забыли мы про то, что в качестве результата вызов std::async возвращает std::future. Если бы мы написали наш пример следующим образом, то результат на экране стал бы действительно неопределённым:

  #include <future>
  #include <iostream>
  #include <thread>

  int main(int argc, char* argv[]) {
      int count = 10;

      auto future1 = std::async(std::launch::async, [&count] {
          for(int i=0; i<count; ++i) {
            std::cout << 1;
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
        }
    });
    auto future2 = std::async(std::launch::async, [&count] {
        for(int i=0; i<count; ++i) {
            std::cout << 2;
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
        }
    });

    return 0;
}

Вот теперь на экране действительно может быть любая последовательность из перемешанных двадцати цифр 1 и 2. Почему результат так кардинально изменился, стоило нам только лишь сохранить std::future, которое вернул вызов std::async?

Как говорится, всё законно, всё по стандарту

Стандарт гарантирует, что окончание выполнение потока, запущенного вызовом std::async, синхронизировано с вызовом получения результата std::future::get или с освобождением общего состояния (shared state) – области памяти, ответственной за передачу результата между std::async и std::future.

В первом примере автоматическое удаление временного объекта std::future, который был возвращён из первого вызова std::async, приводит к освобождению общего состояния и автоматической синхронизации двух потоков. Просто не сохранив результат вызова std::async, мы получили ожидание – второй поток не начнёт выполнение до окончания выполнения первого потока.

Есть вопрос? Напишите в комментариях!

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
0 комментариев
Для комментирования необходимо авторизоваться