Я изучаю мьютексы в C ++, и у меня возникла проблема со следующим кодом (взятым из «Стандартной библиотеки C ++» Н. Йосуттиса).

Я не понимаю, почему он блокирует / выбрасывает , если я не добавлю this_thread::sleep_for в основной поток (тогда он не блокируется и выполняются все три вызова).

Компилятор - cl.exe, используемый из командной строки.

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

std::mutex printMutex;

void print(const std::string& s)
{
    std::lock_guard<std::mutex> lg(printMutex);

    for (char c : s)
    {
        std::cout.put(c);
    }
    std::cout << std::endl;
}

int main()
{
    auto f1 = std::async(std::launch::async, print, "Hello from thread 1");
    auto f2 = std::async(std::launch::async, print, "Hello from thread 2");

    // std::this_thread::sleep_for(std::chrono::seconds(1));

    print(std::string("Hello from main"));       
}
9
Wojtek 9 Окт 2014 в 16:46

3 ответа

Лучший ответ

Я думаю, что вы видите проблему с соответствием реализации MSVC async (в сочетании с future). Я считаю, что это не соответствует. Я могу воспроизвести его с помощью VS2013, но не могу воспроизвести проблему с помощью gcc.

Сбой происходит из-за того, что основной поток завершает работу (и начинает очистку) до завершения двух других потоков.

Следовательно, простая задержка (sleep_for) или .get() или .wait() на двух фьючерсах должна исправить это за вас. Итак, модифицированный main может выглядеть так;

int main()
{
    auto f1 = std::async(std::launch::async, print, "Hello from thread 1");
    auto f2 = std::async(std::launch::async, print, "Hello from thread 2");

    print(std::string("Hello from main"));       

    f1.get();
    f2.get();
}

Отдавайте предпочтение явному ожиданию или избавьтесь от отсроченного «сна».

Примечания к соответствию

Было предложение от Херба Саттера для изменения ожидания или блокировки общего состояния future, возвращаемого из async. Это может быть причиной поведения в MSVC, его можно рассматривать как реализацию предложения. Я не уверен, каков был окончательный результат предложения или его интеграции (или ее части) в C ++ 14. По крайней мере, w.r.t. блокировка future, возвращенная из async, похоже, поведение MSVC не вошло в спецификацию.

Интересно отметить, что формулировка в §30.6.8 / 5 изменилась;

Из C ++ 11

вызов функции ожидания на асинхронном возвращаемом объекте, который разделяет созданное совместно используемое состояние этим вызовом async блокируется до тех пор, пока связанный поток не завершится, как если бы он был присоединен

На C ++ 14

вызов функции ожидания на асинхронном возвращаемом объекте, который разделяет созданное совместно используемое состояние этим вызовом async блокируется до тех пор, пока связанный поток не завершится, как если бы он был присоединен, или иначе время из

Я не уверен, как будет указан «тайм-аут», я полагаю, что это определяется реализацией.

11
Community 23 Май 2017 в 11:49

std::async возвращает будущее. Его деструктор блокирует, если get или wait не был вызван:

он может блокироваться, если выполняются все следующие условия: общее состояние было создано вызовом std :: async, общее состояние еще не готово, и это была последняя ссылка на разделяемое состояние.

См. std :: futures из std :: async aren не особенный! для более подробного рассмотрения этого вопроса.

1
Maxim Egorushkin 9 Окт 2014 в 12:52

Добавьте эти две строки в конец main:

f1.wait();
f2.wait();

Это гарантирует, что потоки завершатся до того, как появится main.

1
egur 9 Окт 2014 в 12:58