【问题标题】:multiple threads able to get flock at the same time多个线程能够同时获得flock
【发布时间】:2012-03-16 18:42:01
【问题描述】:

我的印象是flock(2)是线程安全的,我最近在代码中遇到了这样一个案例,多个线程能够在同一个文件上获得一个锁,这些都是同步使用获得独占使用 c api 群锁定。进程 25554 是具有 20 个线程的多线程应用程序,当死锁发生时,锁定同一文件的线程数会有所不同。多线程应用程序 testEvent 是文件的写入者,而推送是文件中的读取者。不幸的是,lsof 没有打印 LWP 值,所以我找不到持有锁的线程。当下面提到的情况发生时,进程和线程都被困在 flock 调用上,如 pid 25569 和 25554 上的 pstackstrace 调用所示。有关如何在 RHEL 4.x 中克服此问题的任何建议。

我想更新的一件事是,flock 并不总是行为不端,当消息的 tx 速率超过 2 mbps 时,我才遇到与 flock 的死锁问题,低于该 tx 速率的所有内容都是文件。当我增加向 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;

上面的方法会在测试的main函数中的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 ;
      }  
   }     
}

【问题讨论】:

标签: c linux glibc flock


【解决方案1】:

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

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

【讨论】:

  • 感谢您的回答我确信在同一行中考虑将互斥锁与羊群相关联,困扰我的是羊群(2)位于 fcntl(2)之上,而 fcntl(2 ) lockf(2) 声称是线程安全的,所以令我惊讶的是这不是。
  • 我现在已经尝试过这个建议,有额外的 pthread_mutex_t 用于同步群调用,现在发生的事情是 19 个线程被卡在试图获取 pthread_mutex_t 并且 1 个线程无法获得独占锁定使用flock的文件并卡在flock(fd,LOCK_EX);
  • 你从失败的羊群中得到了什么错误?
【解决方案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() 的语义将不同于那些 在本手册页中描述。

【讨论】:

  • 如果你看到我的 lsof 输出文件描述符对于每个线程都是唯一的,那就不是真的 chris。
  • 进程中的线程共享文件描述符。请参阅stackoverflow.com/questions/6223776/… EDIT ---- 而且我认为我们大多数编写过多线程程序的人都编写过工作代码,其中文件描述符在线程之间共享。
  • @user1235176 也许我遗漏了一些东西,但我假设,您会看到不同的文件描述符,因为您为每个线程单独创建它们?我没有看到 fd 被传递,但我可能遗漏了一些东西。
猜你喜欢
  • 1970-01-01
  • 2017-05-17
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
  • 2011-08-03
  • 2017-03-08
  • 2021-08-11
  • 2015-03-02
相关资源
最近更新 更多