多个线程能够同时获得flock

时间:2012-02-27 09:16:59

标签: c linux glibc flock

我的印象是flock(2)是线程安全的,我最近在代码中遇到了这种情况,其中多个线程能够锁定同一个文件,这些文件都与使用同步使用c api flock获得独占锁。过程25554是具有20个线程的多线程应用程序,当发生死锁时,锁定到同一文件的线程数量变化。多线程app testEvent 是文件的编写者,其中push是文件中的reader。不幸的是lsof没有打印LWP值,所以我找不到哪个是持有锁的线程。当下面提到的情况发生时,进程和线程都停留在flock调用上,如pid 25569和25554上的pstackstrace调用所示。有关如何在RHEL 4中解决此问题的任何建议。X。

我想要更新的一件事是flock不会一直行为不端,当消息的tx速率超过2 mbps时我才进入flock的这个死锁问题,低于tx速率一切都是文件。我保持num_threads = 20,size_of_msg = 1000字节不变,只是将每秒tx的消息数从10条消息开始改为100条消息,即20 * 1000 * 100 = 2 mbps,当我将消息数量增加到150然后发生flock问题。

我只是想问一下你对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 ;
      }  
   }     
}

2 个答案:

答案 0 :(得分:9)

flock(2)被记录为“阻止另一个进程持有不兼容的锁”  并且“flock()创建的锁与打开的文件表条目相关联”,因此应该预期同一进程的多个线程的flock - ed锁不会交互。 (flock文档没有提到线程)。

因此,解决方案对您来说应该很简单:将一个pthread_mutex_t关联到每个flock能力的文件描述符,并使用该互斥锁保护对flock的调用。如果您想要读取与写入锁定,也可以使用pthread_rwlock_t

答案 1 :(得分:2)

来自flock(2)的Linux手册页:

  

flock()创建的锁与打开的文件表条目相关联。          这意味着重复的文件描述符(例如,由          fork(2)或dup(2))引用相同的锁,并且可以使用任何这些描述符修改或释放此锁。此外,锁          通过对其中任何一个的显式LOCK_UN操作释放          重复描述符,或所有这些描述符都已关闭。

此外,flock锁没有'堆栈',所以如果你试图获取一个你已经拥有的锁,那么flock调用就是一个noop,它会立即返回而不会阻塞,并且不会以任何方式改变锁状态。

由于进程中的线程共享文件描述符,您可以从不同的线程多次聚集文件,并且它不会阻塞,因为锁已经被保存。

同样来自flock(2)的注释:

  

flock()和fcntl(2)锁具有相同的语义          分叉进程和dup(2)。在使用flock()的系统上          fcntl(2),flock()的语义将与那些不同          在本手册页中进行了描述。