Пример неправильного использования std :: memory_order :: relaxed в C ++ Standard [gorithms.parallel.exec / 5 в n4713] - PullRequest
3 голосов
/ 08 октября 2019

Один из примеров неправильного использования 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
});

И затем он говорит:

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

Вопросы:

  1. В комментарии говорится: «неверно:предполагает выполнение заказа ". Что такое «предполагаемый порядок исполнения»? Я пропускаю это.

  2. Что означают «итерации» в «Приведенном выше примере зависит от порядка выполнения итераций»? Означает ли это итерацию в цикле while? Или это относится к итерации std::for_each?

  3. Если итерации std::for_each выполняются параллельно разными потоками, разве не верно, что одна из итераций/ темы не выходят? Поскольку x.fetch_add(1, std::memory_order::relaxed) является атомарным, и поэтому один поток будет составлять x 1, а другой - x 2, и невозможно иметь x == 1 для обоих потоков. Нет?

1 Ответ

5 голосов
/ 08 октября 2019

"неверно: предполагается выполнение заказа". Что такое «предполагаемый порядок выполнения»?

Предполагается, что тело лямбды выполняется несколькими потоками, а не одним. Стандарт скорее говорит, что он может выполняться параллельно.

Что означают «итерации» в «Приведенном выше примере зависит от порядка выполнения итераций»?

Вероятно, это относится к выполнению лямбды другим потоком. Но стандарт не гарантирует наличия другого потока. См. execute_policy_tag_t :

parallel_policy Тип политики выполнения, используемый в качестве уникального типа для устранения неоднозначности перегрузки параллельного алгоритма и указывает, что выполнение параллельного алгоритма может быть распараллелено. Вызовы функций доступа к элементам в параллельных алгоритмах, вызываемых этой политикой (обычно задаются как std :: execute :: par), разрешено выполнять либо в вызывающем потоке, либо в потоке, неявно созданном библиотекой для поддержки параллельного алгоритма. выполнение. Любые такие вызовы, выполняющиеся в одном и том же потоке, неопределенно упорядочены друг относительно друга.

...