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

l C ++ использует пул потоков Windows (VistaCreateThreadpoolWork если доступно иQueueUserWorkItem если нет) при звонкеstd::async сstd::launch::async.

Количество потоков в пуле ограничено. Если создать несколько задач, которые выполняются в течение длительного времени без сна (включая выполнение операций ввода-вывода), предстоящие задачи в очереди не смогут работать.

Стандарт (я использую N4140) говорит, что используяstd::async сstd::launch::async

... звонкиINVOKE(DECAY_COPY(std::forward<F>(f)), DECAY_COPY(std::forward<Args>(args))...) (20.9.2, 30.3.1.2)как будто в новом потоке выполнения, представленном объектом потока с призывами кDECAY_COPY() оценивается в потоке, который называетсяasync.

(§30.6.8p3, Выделение мое.)

std::threadконструктор создает новый поток и т. д.

О потоках вообще сказано (§1.10p3):

Реализации должны обеспечить прогресс всех разблокированных потоков. [Замечания: Стандартные библиотечные функции могут автоматически блокировать ввод-вывод или блокировки. Факторы в среде выполнения, включая внешние приоритеты потоков, могут помешать реализации обеспечить определенные гарантии продвижения вперед. -конечная нота]

Если я создам кучу потоков ОС илиstd::threads, все они выполняют некоторые очень длинные (возможно, бесконечные) задачи, все они будут запланированы (по крайней мере, в Windows; без вмешательства в приоритеты, сходства и т. д.). Если мы планируем те же задачи для пула потоков Windows (или используемstd::async(std::launch::async, ...) что делает это), более поздние запланированные задачи не будут выполняться, пока предыдущие задачи не будут завершены.

Строго говоря, это законно? А что значит "в конце концов"?

Проблема в том, что если задачи, запланированные в первую очередь,де-факто бесконечное, остальные задачи не будут выполняться. Таким образом, другие потоки (не потоки ОС, а «C ++ - потоки» согласно правилу «как будто») не будут прогрессировать.

Можно утверждать, что, если код имеет бесконечные циклы, поведение не определено, и, следовательно, это законно.

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

(У меня есть куча асинхронных вызовов, выполняющих следующую лямбду

auto lambda = [&] {
    while (m.try_lock() == false) {
        for (size_t i = 0; i < (2 << 24); i++) {
            vi++;
        }
        vi = 0;
    }
};

и блокировка снята только после ввода пользователя. Но есть и другие допустимые виды допустимых бесконечных циклов.)

Если я планирую пару таких задач, задачи, которые я планирую после них, не запускаются.

Действительно злым примером будет запуск слишком большого количества задач, которые выполняются до тех пор, пока не будет снята блокировка / не будет установлен флаг, а затем запланируйте с помощью `std :: async (std :: launch :: async, ...) задачу, которая поднимает флаг , Если слово «в конце концов» не означает чего-то очень удивительного, эта программа должна завершиться. Но при реализации VC ++ это не так!

Мне это кажется нарушением стандарта. Что меня удивляет, так это второе предложение в записке. Факторы могут помешать реализации сделать определенные гарантии продвижения вперед. Итак, как эти реализации соответствуют?

Это все равно что сказать, что могут быть факторы, мешающие реализациям обеспечивать определенный аспект упорядочения памяти, атомарности или даже существования нескольких потоков выполнения. Замечательные, но соответствующие размещенные реализации должны поддерживать несколько потоков. Слишком плохо для них и их факторов. Если они не могут предоставить их, это не C ++.

Это ослабление требования? Если интерпретировать так, это полное снятие требования, так как оно не определяет, какие факторы и, что более важно, какие гарантии могут не предоставляться реализациями.

Если нет - что вообще означает эта записка?

Я помню, что сноски были ненормативными в соответствии с директивами ISO / IEC, но я не уверен насчет примечаний. Я нашел в директивах ISO / IEC следующее:

24 ноты

24.1 Цель или обоснование

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

Акцент мой. Если я рассматриваю документ без этой неясной записки, мне кажется, что темы должны прогрессировать,std::async(std::launch::async, ...) имеет эффекткак будто функтор выполняется в новом потоке, как если бы он был создан с использованиемstd::threadи, следовательно, функторы, отправленные с использованиемstd::async(std::launch::async, ...) должен добиться прогресса. И в реализации VC ++ с пулом потоков они этого не делают. Таким образом, VC ++ является нарушением стандарта в этом отношении.

Полный пример, протестированный с использованием VS 2015U3 на Windows 10 Enterprise 1607 на i5-6440HQ:

#include <iostream>
#include <future>
#include <atomic>

int main() {
    volatile int vi{};
    std::mutex m{};
    m.lock();

    auto lambda = [&] {
        while (m.try_lock() == false) {
            for (size_t i = 0; i < (2 << 10); i++) {
                vi++;
            }
            vi = 0;
        }
        m.unlock();
    };

    std::vector<decltype(std::async(std::launch::async, lambda))> v;

    int threadCount{};
    std::cin >> threadCount;
    for (int i = 0; i < threadCount; i++) {
        v.emplace_back(std::move(std::async(std::launch::async, lambda)));
    }

    auto release = std::async(std::launch::async, [&] {
        __asm int 3;
        std::cout << "foo" << std::endl;
        vi = 123;
        m.unlock();
    });

    return 0;
}

С 4 или меньше это заканчивается. С более чем 4 это не так.

Подобные вопросы:

Есть ли реализация std :: async, которая использует пул потоков? - Но это не вопрос о законности, и в любом случае не имеет ответа.

std :: async - использование зависит от реализации? - Упоминается, что «пулы потоков действительно не поддерживаются», но фокусируется наthread_local переменные (которые разрешимы, даже если «не просто» или нетривиально, как говорится в ответе и комментарии) и не обращаются к примечанию рядом с требованием достижения прогресса.

Ответы на вопрос(1)

Ваш ответ на вопрос