Несколько потоков, способных одновременно получать стадо

У меня создалось впечатление, что flock (2) является потокобезопасным, я недавно столкнулся с ситуацией в коде, где несколько потоков могут получить блокировку в том же файле, которые все синхронизированы с использованием получения эксклюзивной блокировки с помощью c api flock. Процесс 25554 представляет собой многопоточное приложение, которое имеет 20 потоков, количество потоков, имеющих блокировку для одного и того же файла, изменяется, когда происходит тупиковая ситуация. Многопоточное приложение testEvent приложения является писателем для файла, где был нажат читатель из файла. К сожалению, lsof не печатает значение LWP, поэтому я не могу найти, какие потоки хранят блокировку. Когда происходит указанное ниже условие, процесс и потоки застревают в вызове стаи, отображаемом при вызове pstack или strace на pid 25569 и 25554. Любые предложения о том, как преодолеть это в RHEL 4.x.

Одна вещь, которую я хотел обновить, - это стая, которая не ошибается все время, когда скорость обмена сообщениями составляет более 2 Мбит/с только тогда, когда я попадаю в эту тупиковую проблему со ставкой, ниже этой скорости tx все является файлом. Я сохранил константу num_threads= 20, size_of_msg= 1000bytes и просто изменил количество сообщений tx в секунду, начиная с 10 сообщений до 100 сообщений, которое составляет 20 * 1000 * 100 = 2 mbps, когда я увеличиваю количество сообщения до 150, тогда возникает проблема стайки.

Я просто хотел спросить, каково ваше мнение о flockfile c api.

 sudo lsof filename.txt
    COMMAND       PID     USER     FD       TYPE     DEVICE     SIZE   NODE       NAME
    push         25569    root     11u       REG      253.4      1079   49266853   filename.txt
    testEvent    25554    root     27uW      REG      253.4      1079   49266853   filename.txt
    testEvent    25554    root     28uW      REG      253.4      1079   49266853   filename.txt
    testEvent    25554    root     29uW      REG      253.4      1079   49266853   filename.txt
    testEvent    25554    root     30uW      REG      253.4      1079   49266853   filename.txt

Многопоточная тестовая программа, которая вызовет функцию write_data_lib_func lib.

void* sendMessage(void *arg)  {

int* numOfMessagesPerSecond = (int*) arg;
std::cout <<" Executing p thread id " << pthread_self() << std::endl;
 while(!terminateTest) {
   Record *er1 = Record::create();
   er1.setDate("some data");

   for(int i = 0 ; i <=*numOfMessagesPerSecond ; i++){
     ec = _write_data_lib_func(*er1);
     if( ec != SUCCESS) {
       std::cout << "write was not successful" << std::endl;

     }

   }
   delete er1;
   sleep(1);
 }

 return NULL;

Вышеуказанный метод будет вызываться в pthreads в основной функции теста.

for (i=0; i<_numThreads ; ++i) {
  rc = pthread_create(&threads[i], NULL, sendMessage, (void *)&_num_msgs);
  assert(0 == rc);

}

Вот источник писателя/читателя, из-за соображений собственности, которые я не хотел просто вырезать и вставлять, источник записи будет обращаться к нескольким потокам процесса

int write_data_lib_func(Record * rec) {      
if(fd == -1 ) {  
    fd = open(fn,O_RDWR| O_CREAT | O_APPEND, 0666);
} 
if ( fd >= 0 ) {
   /* some code */ 

   if( flock(fd, LOCK_EX) < 0 ) {
     print "some error message";
   }
   else { 
    if( maxfilesize) {
      off_t len = lseek ( fd,0,SEEK_END);
      ...
      ... 
      ftruncate( fd,0);
      ...
      lseek(fd,0,SEEK_SET); 
   } /* end of max spool size */ 
   if( writev(fd,rec) < 0 ) {
     print "some error message" ; 
   }

   if(flock(fd,LOCK_UN) < 0 ) {
   print some error message; 
   } 

В стороне читателя есть процесс демона без потоков.

int readData() {
    while(true) {
      if( fd == -1 ) {
         fd= open (filename,O_RDWR);
      }
      if( flock (fd, LOCK_EX) < 0 ) { 
        print "some error message"; 
        break; 
      } 
      if( n = read(fd,readBuf,readBufSize)) < 0 ) { 
        print "some error message" ;
        break;
      }  
      if( off < n ) { 
        if ( off <= 0 && n > 0 ) { 
          corrupt_file = true; 
        } 
        if ( lseek(fd, off-n, SEEK_CUR) < 0 ) { 
          print "some error message"; 
        } 
        if( corrupt_spool ) {  
          if (ftruncate(fd,0) < 0 ) { 
             print "some error message";
             break;
           }  
        }
      }
      if( flock(fd, LOCK_UN) < 0 ) 
       print some error message ;
      }  
   }     
}

Ответ 1

flock (2) документируется как "блокировка, если несовместимая блокировка удерживается другим процессом"  и "блокировки, созданные flock(), связаны с записью таблицы открытых файлов", поэтому следует ожидать, что блокировки flock -ed несколькими потоками одного и того же процесса не будут взаимодействовать. (flock документация не упоминает потоки).

Следовательно, решение должно быть простым для вас: связать один pthread_mutex_t с каждым дескриптором файла flock и защитить вызов flock с помощью этого мьютекса. Вы также можете использовать pthread_rwlock_t, если хотите прочитать блокировку чтения и записи.

Ответ 2

На странице man man для flock (2):

Замки, созданные flock(), связаны с записью открытой таблицы файлов.        Это означает, что дубликаты дескрипторов файлов (созданные, например,        fork (2) или dup (2)) относятся к одной и той же блокировке, и эта блокировка может быть изменена или выпущена с использованием любого из этих дескрипторов. Кроме того, замок        освобождается либо явной операцией LOCK_UN на любом из этих        дублировать дескрипторы или когда все такие дескрипторы были закрыты.

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

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

Также из заметок о стаи (2):

flock() и fcntl (2) блокировки имеют разную семантику относительно        раздвоенные процессы и dup (2). В системах, реализующих flock(), используя        fcntl (2), семантика flock() будет отличаться от семантики        описанных на этой странице руководства.