﻿# Будьте аккуратны с директивой atomic

Хочется рассказать об одной ошибке, которую легко может допустить человек еще малознакомый с технологией OpenMP\. Ошибка связана с неверным представлением о работе директивы atomic\. Директива atomic работает быстрее, чем критические секции, так как некоторые атомарные операции могут быть напрямую заменены командами процессора\. И поэтому ее удобно использовать при вычислении различных выражений\. Но следует помнить, что atomic никак не влияет на вызов функций, используемых в выражении\.

Рассмотрим это на примере:

```cpp
class Example
{
public:
  unsigned m_value;
  Example() : m_value(0) {}
  unsigned GetValue()
  {
    return ++m_value;
  }
  unsigned GetSum()
  {
    unsigned sum = 0;
    #pragma omp parallel for
    for (ptrdiff_t i = 0; i < 100; i++)
    {
      #pragma omp atomic
      sum += GetValue();
    }
    return sum;
  }
};
```

Данный пример содержит ошибку [состояния гонки](https://pvs-studio.ru/ru/blog/terms/0042/) \(Race condition\), и возвращаемое ей значение может меняться от запуска к запуску\. Если вы попробуете приведенный пример, и результат все время будет верен, то можете изменить функцию GetValue, как показано ниже, чтобы ошибка проявлялась более четко:

```cpp
unsigned GetValue()
{
  Sleep(0);
  m_value++;
  Sleep(0);
  return m_value;
}
```

В коде с помощью директивы atomic защищено увеличение переменной "sum"\. Но директива atomic не оказывает влияние на вызов функции GetValue\(\)\. Вызовы происходят в параллельных потоках, что приводит к ошибкам при выполенения операции "\+\+m\_value" внутри функции GetValue\.

Помните, что функции, используемые в выражениях, к которым применяется директива atomic, должны быть потоко\-безопасными \(thread\-safe\)\. Директива atomic распространяется только на операции следующего вида:

* _x binop\= expr_
* _x\+\+_
* _\+\+x_
* _x−−_
* _−−x_

Здесь х \- скалярная переменная, expr \- выражение со скалярными типами, в котором не присутствует переменная х, binop \- не перегруженный оператор \+, \*, \-, /, &, ^, \|, <<, or \>\>\. Во всех остальных случаях применять директиву atomic нельзя\.

В приведенном примере директива atomic обезопасит операцию "sum \+\=", но не обезопасит вызов функции GetValue\. Для исправления показанной ошибки необходимо использовать критическую секцию или иные способы для защиты переменной m\_value\.