Гонка данных в распараллеленном std :: for_each - PullRequest
3 голосов
/ 30 октября 2019

На ссылочном веб-сайте cpp по политике выполнения есть такой пример:

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);
  while (x.load(std::memory_order_relaxed) == 1) { } // Error: assumes execution order
});

Как вы видите, это пример (предположительно) ошибочного кода. Но я не очень понимаю, в чем здесь ошибка, мне не кажется, что какая-то часть кода предполагает порядок выполнения. AFAIK, первый поток на fetch_add будет ждать второй поток, но это не проблема. Я что-то упустил и там какая-то ошибка?

1 Ответ

3 голосов
/ 30 октября 2019

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

Насколько я могу видеть, проблема здесь в том, что нет гарантии, сколько потоков используется, еслисистема использует один поток - здесь будет бесконечный цикл (while (x.load(std::memory_order_relaxed) == 1) { } никогда не завершится).
Так что, я думаю, комментарий означает, что этот код ошибочно полагается на выполнение нескольких потоков, что приведет к вызову fetch_addв какой-то момент более одного раза.
Единственная гарантия, которую вы получаете, заключается в том, что для каждого потока вызовы не чередуются.

...