Могу ли я изменить данные с помощью std :: shared_lock на std :: shared_mutex?

У меня есть несколько буферов, совместно используемых несколькими потоками чтения / записи, и разные писатели изменяют данные по-разному.

Например, Writer1 просто добавляет новые данные, а Writer2 увеличивает размер буфера (перераспределяет память и перемещает данные).

Если я поставлю один мьютекс для синхронизации всех обращений к данным, производительность может быть не лучше, потому что большинству читателей просто нужно читать один буфер, а большинству писателей просто нужно записать небольшой фрагмент данных в один буфер.

Если я подготовлю один мьютекс для каждого буфера, отношения блокировки / разблокировки между потоками будут более сложными.

Теперь я хочу кое-что подтвердить:

Если писатель изменяет данные только с помощью shared_lock на мьютексе, будут ли другие видеть грязные данные с unique_lock / shared_lock на том же мьютексе?

Я закодировал экспериментальную программу, как показано ниже, и она выглядит безошибочной, но я все еще не осмеливаюсь использовать ее в продукте.

atomic_bool    g_abShouldRun = true;
sem_t          g_semDoIt1;
sem_t          g_semDone1;
sem_t          g_semDoIt2;
sem_t          g_semDone2;
shared_mutex   g_mutex;
int g_iX = 3, g_iY = 9, g_iR1 = 1, g_iR2 = 3;

void writer() {
   std::srand( 8 );

   while( g_abShouldRun ) {
      sem_wait( &g_semDoIt1 );
      while( rand() % 8 != 0 )
         ;

      {
         shared_lock<shared_mutex> lk( g_mutex );
         g_iX *= 2;
         g_iY *= 2;
      }
      sem_post( &g_semDone1 );
   };
};

void reader() {
   std::srand( 8 );

   while( g_abShouldRun ) {
      sem_wait( &g_semDoIt2 );
      while( rand() % 8 != 0 )
         ;

      {
         unique_lock<shared_mutex> lk( g_mutex );
         g_iR1 = g_iX;
         g_iR2 = g_iY;
      }
      sem_post( &g_semDone2 );
   };
};

int main( int argc, char** argv ) {
   int iLasting = 10, iError = 0;
   if( argc > 1 )
      iLasting = atoi( argv[1] );
   steady_clock::time_point tpEnd = steady_clock::now() + seconds( iLasting );

   if( sem_init( &g_semDoIt1, 0, 0 ) || sem_init( &g_semDone2, 0, 0 ) ||
         sem_init( &g_semDoIt2, 0, 0 ) || sem_init( &g_semDone2, 0, 0 ) ) {
      cerr << "Failed to create semaphors." << endl;
      return EXIT_FAILURE;
   }

   thread thd1( writer );
   thread thd2( reader );

   while( steady_clock::now() < tpEnd ) {
      sem_post( &g_semDoIt1 );
      sem_post( &g_semDoIt2 );
      sem_wait( &g_semDone1 );
      sem_wait( &g_semDone2 );
      if( g_iR1 * 3 != g_iR2 )
         ++iError;
   }
   g_abShouldRun = false;
   sem_post( &g_semDoIt1 );
   sem_post( &g_semDoIt2 );
   thd1.join();
   thd2.join();
   sem_destroy( &g_semDoIt1 );
   sem_destroy( &g_semDoIt2 );
   sem_destroy( &g_semDone1 );
   sem_destroy( &g_semDone2 );
   cout << "Error:" << iError << endl;
   return EXIT_SUCCESS;
};

person Leon    schedule 12.05.2020    source источник


Ответы (1)


При беглом рассмотрении выявляются следующие проблемы:

  1. измените код на использование unique_lock при записи;
  2. измените код на использование shared_lock при чтении;
  3. не изменяйте другие общие глобальные переменные при чтении - они практически будут записываться, только в другом месте;
  4. сколько {shared_mutexs, функция с использованием unique_lock, функция с использованием shared_lock} кортежей вы будете использовать с несколькими потоками и несколькими буферами, вам нужно выяснить самостоятельно, но это будет от 1 до количества буферов.
person catalin    schedule 12.05.2020
comment
Спасибо за вашу помощь! Но я не собираюсь исправлять образцы кодов, мне нужно подтвердить свою мысль. - person Leon; 12.05.2020
comment
Я действительно не знаю, о чем ты думаешь. Вы используете shared_lock при записи и unique_lock при чтении. Это концептуально неверно, но в этом конкретном случае при использовании одного писателя он все равно будет работать. При использовании нескольких писателей это однозначно выйдет из строя. - person catalin; 12.05.2020
comment
Да, я знаю, что одновременно может писать только один писатель. Фактически, есть еще один мьютекс, который гарантирует, что только один писатель будет проходить в одно и то же время, прежде чем он заблокирует этот shared_mutex. Поскольку логика кода моего продукта слишком сложна, я просто показываю пример программы. - person Leon; 12.05.2020