Пример неправильного использования std::memory_order:: Relaxed в стандарте C++ [алгоритмы.parallel.exec / 5 в n4713]
Один из примеров неправильного использования std::memory_order::relaxed
в стандарте C++:
std::atomic<int> x{0};
int a[] = {1,2};
std::for_each(std::execution::par, std::begin(a), std::end(a), [&](int) {
x.fetch_add(1, std::memory_order::relaxed);
// spin wait for another iteration to change the value of x
while (x.load(std::memory_order::relaxed) == 1) { } // incorrect: assumes execution order
});
И тогда он говорит:
Приведенный выше пример зависит от порядка выполнения итераций и не завершится, если обе итерации выполняются последовательно в одном и том же потоке выполнения.
Вопросы:
В комментарии говорится: "неверно: предполагается порядок выполнения". Что такое "предполагаемый порядок исполнения"? Я скучаю по нему.
Что означает "итерация" в "Приведенном выше примере зависит от порядка выполнения итераций"? Означает ли это итерацию в цикле while? Или это относится к итерации
std::for_each
?Если итерации
std::for_each
выполняются параллельно разными потоками, правда ли, что одна из итераций / потоков не завершится? Потому какx.fetch_add(1, std::memory_order::relaxed)
является атомарным, поэтому один поток сделаетx
1 и еще один сделаютx
2, и невозможно иметь x == 1 для обоих потоков. Нет?
1 ответ
"неверно: предполагается порядок исполнения". Что такое "предполагаемый порядок исполнения"?
Предполагается, что тело лямбды выполняется несколькими потоками, а не одним. Стандарт скорее говорит, что он может выполняться параллельно.
На что ссылаются "итерации" в "Приведенном выше примере зависит от порядка выполнения итераций"?
Вероятно, это относится к выполнению лямбды другим потоком. Но стандарт не гарантирует наличия другого потока. См. Execution_policy_tag_t:
parallel_policy
Тип политики выполнения, используемый как уникальный тип для устранения неоднозначности перегрузки параллельного алгоритма и индикации того, что выполнение параллельного алгоритма может быть распараллелено. Вызовы функций доступа к элементам в параллельных алгоритмах, вызываемых с помощью этой политики (обычно указываемой как std::execution::par), могут выполняться либо в вызывающем потоке, либо в потоке, неявно созданном библиотекой для поддержки выполнения параллельного алгоритма. Любые такие вызовы, выполняемые в одном потоке, имеют неопределенную последовательность по отношению друг к другу.