Std :: async вызывается не сразу

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

#include <iostream>
#include <future>
#include <thread>
#include <vector>
#include <chrono>
#include <string>

using namespace std;

mutex m;

void print(string s)
{
    lock_guard<mutex> l(m);
    cout << s << endl;
}

int main() {
    print("main thread1");

    std::future<int> f = std::async(launch::async, [](){
        print("async");
        return 1;
    });

    print("main thread2");
    int i = f.get();
    print("main thread3");

    return 0;
}

Результат, которого я ожидаю, будет следующим:

main thread1
async
main thread2
main thread3

Но реальный результат выглядит так:

main thread1
main thread2
async
main thread3

Не могли бы вы объяснить, почему лямбда вызывается только при вызове будущего get()? Если я поставлю sleep_for перед main thread2, то это будет то, чего я ожидал.

Лямбда называется асинхронно. Он может быть вызван до, после или одновременно с любой частью main между вызовами std::async и std::future::get.

n. 1.8e9-where's-my-share m. 18.08.2018 15:53
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
473
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Вызов async возвращается, как только новый поток становится созданный. Теперь у вас есть два отдельных потока, и единственная корреляция между операциями в этих двух потоках состоит в том, что поток, созданный вызовом async, должен быть завершен до того, как вызов f.get() вернется. Итак, поведение, которое вы видите, соответствует требованиям. Ожидаемое поведение также соответствует требованиям. В этом суть запуска отдельных потоков: они работают независимо, пока вы не сделаете что-нибудь для их синхронизации.

Итак, если я понимаю, что лямбда вызывается как можно скорее, а позже возвращаемые данные извлекаются из второго потока? Значит, утверждение «лямбда запускается только при вызове get()» неверно?

drewpol 18.08.2018 16:18

Функция потока вызывается всякий раз, когда к ней обращается планировщик потоков. Если поток не завершился (вызовом и возвратом из функции потока) в то время, когда вызывается get(), вызов get() блокируется до завершения потока.

Pete Becker 18.08.2018 16:53

Ваше ожидание показывает, что вы действительно не понимаете, что такое асинхронность. Давайте посмотрим на следующий алгоритм:

operation1
asynchronous operation2
operation3

Здесь операция1 выполняется до того, как будет выполнен асинхронный вызов операции2. Когда операция2 вызывается асинхронно, создается новый поток, и его конец не ожидается. Если операция3 ожидает, что операция2 уже будет выполнена, то это ошибка в коде. Если вам нужен результат данного потока, вам нужно будет с ним синхронизироваться.

Спасибо за ответ. Да я понимаю о чем ты. Так почему же, когда я отлаживаю код и устанавливаю точку останова на get() и во время выполнения этой строки начинает выполняться лямбда? Разве get() не должен только извлекать данные, а не запускать лямбда-выражение?

drewpol 18.08.2018 16:34

@drewpol, потому что поток, открытый асинхронно, выполняется, пока вы смотрите на код. Мы, люди, слишком медлительны для подобных вещей.

Lajos Arpad 18.08.2018 17:30

Попробуй это:

#include <iostream>
#include <future>
#include <thread>
#include <vector>
#include <chrono>
#include <string>

using namespace std;
using namespace std::chrono;

mutex m;

void print(string s)
{
    lock_guard<mutex> l(m);
    nanoseconds ms = duration_cast< nanoseconds >(
        system_clock::now().time_since_epoch()
    );
    cout << s << "(" << ms.count() << ")" << endl;
}

int main() {
    print("main thread1");

    future<int> f = async(launch::async, [](){
        print("async");
        return 1;
    });

    //this_thread::sleep_for(2s);
    print("main thread2");
    int i = f.get();
    print("main thread3");

    return 0;
}

Попробуйте поэкспериментировать с функцией сна (this_thread::sleep_for(2s);), чтобы понять, что произошло.

Если вы хотите видеть «async» перед «main thread2» - установите этот спящий режим прямо перед вызовом функции print.

Создание потоков - очень сложная задача для любой платформы и ОС, на это уходит много наносекунд, в то время как просто вывод чего-либо - гораздо более быстрая задача. Таким образом, вы даже можете добавить еще 10 вызовов print - они будут быстрее, чем печать внутри нового потока.

Но f.get() в вашем коде заставляет систему ждать результата. Работает как сон. Итак, это единственная причина, по которой «основной поток3» печатается последним в вашем примере кода.

Я получаю вывод с помощью этого кода:

./a.out 
main thread1(1534603161902827214)
main thread2(1534603161902924375)
async(1534603161902977095)
main thread3(1534603161903114658)

Разница между «основным потоком1» и «основным потоком2» составляет 97161. И разница между 'main thread2' и 'main thread3' составляет 190283. Все выходы находятся в одном потоке, шаг за шагом. Но этот f.get() тормозит последний звонок.

Другой момент. C++ - очень сложный язык, и педантичный синтаксис - ваш помощник в борьбе с ошибками в коде. Здесь вы используете using namespace std;, но все равно пишете пространство имен std::. Это стилистическая ошибка.

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