Состояние гонки с переменной stati c в функции - PullRequest
0 голосов
/ 04 марта 2020

У меня есть следующая функция:

int helper(int id) {
  static std::map<int,int>* lookup_table = new std::map<int,int>();

  // Modify lookup_table

  return (it == lookup_table->end()) ? -1 : 0;
}

helper вызывается в нескольких потоках. Как можно предотвратить состояние гонки для lookup_table?

Было бы достаточно?

static std::mutex helper_lock;

int helper(int id) {
  static std::map<int,int>* lookup_table = new std::map<int,int>();

  helper_lock.lock();
  // Modify lookup_table
  helper_lock.unlock();

  return (it == lookup_table->end()) ? -1 : 0;
}

1 Ответ

1 голос
/ 04 марта 2020

Этого будет недостаточно. Модификации контейнера STL потенциально могут сделать недействительными его итераторы, поэтому сравнение с lookup_table.end() в конце функции может быть недействительным, если другой поток модифицирует таблицу.

Простое решение - разблокировать только после того, как сравнение сделано. (обратите внимание, что я использую std::unique_lock<std::mutex> для выполнения блокировки - блокировка снимается, когда unique_lock выходит из области видимости

static std::mutex helper_lock;

int helper(int id) {
  static std::map<int,int>* lookup_table = new std::map<int,int>();

  std::unique_lock<std::mutex> lock(helper_lock);
  // Modify lookup_table
  // explicit unlock is not required, lock is released when lock is destroyed

  return (it == lookup_table->end()) ? -1 : 0;
}

Более сложным решением является использование shared_mutex, который позволяет блокировка чтения / записи. Это, вероятно, то, что вам нужно в любом случае, поскольку, безусловно, у вас есть процессы, которые читают таблицу поиска? Вы можете применить блокировку чтения до того, как итератор end() будет прочитан и разыменован. Несколько потоков смогут читать данные сразу, но ни одна не может записываться, пока не будут освобождены все читатели.

static std::shared_mutex helper_lock;

int helper(int id) {
  static std::map<int,int>* lookup_table = new std::map<int,int>();

  {
    std::unique_lock<std::shared_mutex> lock(helper_lock);
    // Modify lookup_table
    // explicit unlock is not required, lock is released when lock is destroyed
  }

  // acquire read-only lock, but not if any thread holds a unique_lock
  std::shared_lock<std::shared_mutex> lock(helper_lock);
  return (it == lookup_table->end()) ? -1 : 0;
}
...