在同一线程中多次锁定互斥量

15

我正在开发一个嵌入式Linux操作系统(uClinux)上的应用程序,并且我需要能够在同一线程中多次锁定互斥量。

我定义了一个互斥量(mutex)和一个互斥量属性(mutexattr),并进行了初始化,如下所示:

pthread_mutexattr_t waiting_barcode_mutexattr;
pthread_mutex_t waiting_barcode_mutex;

pthread_mutexattr_init(&waiting_barcode_mutexattr);
pthread_mutexattr_settype(&waiting_barcode_mutexattr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&waiting_barcode_mutex, &waiting_barcode_mutexattr);

但是当我尝试两次获取锁时,它在第二次获取锁时被阻塞:

pthread_mutex_lock(&waiting_barcode_mutex);
pthread_mutex_lock(&waiting_barcode_mutex);

我是不是初始化有误了?或者有更好的方法来完成相同的任务吗?

先行致谢。

结论:

  • 显然,PTHREAD_MUTEX_RECURSIVE或PTHREAD_MUTEX_RECURSIVE_NP不起作用,因此我无法创建可重入互斥锁。
  • try_lock也不行。如果可以获取锁,则会获得锁,并返回错误,如果无法获取锁,则返回错误信息。但不幸的是,该错误只告诉我互斥锁已经在使用中,我无法确定当前线程是否已经拥有锁。
  • pthread_mutex_lock可以返回错误,如果当前线程拥有锁,则需要创建类型为PTHREAD_MUTEX_ERRORCHECK的mutex,但我也无法创建这样一个mutex。

你为什么要尝试多次锁定它?通常,互斥锁被用来确保在任何给定时间只有代码的一部分可以拥有该锁。 - WildCrustacean
基本上这与我设置代码和辅助函数的方式有关。也许我应该改变我的代码并避免这种情况。无论如何,我很好奇。 - Megacan
3
你是否在检查属性和互斥锁初始化调用的返回值?也许你的libc不支持递归锁? - Nikolai Fetissov
你是否正在尝试使用信号量(http://en.wikipedia.org/wiki/Semaphore_(programming%29,http://www.minek.com/files/unix_examples/semab.html)? - Brian
@Nikolai:我忘记测试返回值了,但现在我已经测试过了,它们都返回0。 @Brian:不,我不想使用信号量。 - Megacan
5个回答

10

这不是你所期望的吗?

第一个调用获取锁,第二个将被阻塞直到第一个锁被释放 (pthread_mutex_unlock)。这就是锁的作用。

从文档中可以看出:

"如果互斥量已经被锁定,则调用线程会一直阻塞,直到互斥量可用为止。"

也许你想要使用pthread_mutex_trylock?除非我们知道你想要完成什么任务,否则很难说。

更正:

我没有看到你设置了PTHREAD_MUTEX_RECURSIVE.... 让我再想想。

思考后:

通过搜索google codesearch,似乎并不是所有库都实现了PTHREAD_MUTEX_RECURSIVE。你可以尝试使用PTHREAD_MUTEX_RECURSIVE_NP,或者你可能需要采取一些巧妙的方法来解决这个问题。


我想我得使用trylock。我过去两年一直在使用.Net编程,我一直以为锁默认是可重入的。有一段时间没有用C编程了。 - Megacan

3
似乎pthread互斥锁不可重入。您可以通过设置标志来解决,以指示线程是否已经锁定了互斥锁:
bool haveLock = false;// 线程变量
pthread_mutex_t waiting_barcode_mutex; // 也是线程变量
mylock() { if( haveLock ) return; // 不需要再次锁定 pthread_mutex_lock(&waiting_barcode_mutex); haveLock = true; }
myunlock() { haveLock = false; pthread_mutex_unlock(&waiting_barcode_mutex); // 或任何其他解锁调用 }

2

刚意识到我没有标记这个问题为已回答。

来自问题的结论:

  • 显然,PTHREAD_MUTEX_RECURSIVE或PTHREAD_MUTEX_RECURSIVE_NP无法工作,因此我无法创建可重入互斥锁。
  • try_lock也不好用。它会尝试获取锁,如果可以则获取并返回错误信息,如果无法获取锁,则返回一个错误。不幸的是,错误只告诉我互斥锁已经在使用中,我无法确定当前线程是否已经拥有该锁。
  • pthread_mutex_lock可能会返回错误,如果当前线程拥有锁,则需要创建类型为PTHREAD_MUTEX_ERRORCHECK的互斥锁,但我也无法创建这样的锁。

1

这里是在我的Dell m6300上测试过的,在UBUNTU 12.04 LTS上工作的代码:

  pthread_mutex_t mutex;
  pthread_mutexattr_t attr;
  int rc = pthread_mutexattr_init(&attr);
    if (rc != 0)
        throw (L"pthread_mutexattr_init returns " + rc);
    rc = pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE_NP);
    if (rc != 0)
        throw (L"pthread_mutexattr_settype returns " + rc);
    rc = pthread_mutex_init (&mutex, &attr);
    if (rc != 0)
        throw (L"pthread_mutex_init returns " + rc);
    rc = pthread_mutexattr_destroy(&attr);
    if (rc != 0)
        throw (L"pthread_mutexattr_destroy returns " + rc);

   //first lock
   rc = pthread_mutex_lock(&mutex);
    if (rc != 0)
        throw (L"pthread_mutex_lock returns " + rc);
   //second lock
   rc = pthread_mutex_lock(&mutex);
    if (rc != 0)
        throw (L"pthread_mutex_lock returns " + rc);

不要忘记释放互斥锁,次数应与获取它的次数相同。

1
下面的代码显示,在调用pthread上的unlock之前,多次在同一线程上连续进行锁定一个关键部分没有问题。您可以在解锁之前在同一线程上成功地进行多次锁定,而不必担心,但请注意,这并不是良好的程序员实践。正确的方法是调用lock(),让线程执行critical section,然后调用unlock(),这样其他线程就可以在lock和unlock之间执行相同的代码(称为critical section)。下面的代码使用pthread上的ATTRIBUTES来防止任何程序员的错误。请继续阅读!
// Example program using a thread locking multiple times sequentially before unlocking
#include <iostream>

using namespace std;

pthread_mutexattr_t     _attr;
pthread_mutex_t         _mutex;

///
/// Initialize mutex with error return locking mechanism (does not block
/// its own thread if multiple locks occurs.
///
void InitMutex()
{
   // Initialize mutex
   int ret=0;
   ret = pthread_mutexattr_settype(&_attr, PTHREAD_MUTEX_ERRORCHECK_NP);   // PTHREAD_MUTEX_ERRORCHECK_NP avoids double locking on same thread.
   if(ret != 0)
   {
      printf("Mutex attribute not initialized!!\n");
   }
   ret = pthread_mutex_init(&_mutex, &_attr);
   if(ret != 0)
   {
      printf("Mutex not initialized!!\n");
   }
}

///
/// Locks the critical section
///
int lock_me()
{
   return pthread_mutex_lock(&_mutex);
}

///
/// Unlocks the critical section
///
int unlock_me()
{
   return pthread_mutex_unlock(&_mutex);
}

int main()
{
  InitMutex(); // Very important
  int ret = 0;
  
  ret = lock_me();    // return value of 0 - OK
  cout << "First lock returns: "<< ret<< endl;
  ret = lock_me();    // returns a value like 35 - ERROR, but ignores locking again
  cout << "Second lock returns: "<< ret<< endl;
  
  // Do something in this critical section. No other thread can execute this at this time before unlock. Other threads (if any) wait at lock() waiting for main function to unlock() first.

  ret = unlock_me();  // unlocks the critical section. All is OK
  cout << "First unlock returns: "<< ret<< endl;
  ret = unlock_me();  // returns error value of 1, nothing to lock
  cout << "Second unlock returns: "<< ret<< endl;
  ret = unlock_me();  // same as above, nothing to do. Ignore and move on!
  cout << "Third unlock returns: "<< ret << endl;

  // The main() thread will never have a race condition ;) All iz well!!

  pthread_mutexattr_destroy(&_attr);    // clean up the mutex attribute
  pthread_mutex_destroy(&_mutex);       // clean up the mutex itself

}

输出:

第一个锁返回: 0

第二个锁返回: 35

第一个解锁返回: 0

第二个解锁返回: 1

第三个解锁返回: 1


你有没有看到问题中提到的那一部分:“如果当前线程拥有锁,pthread_mutex_lock 可能会返回错误,但是为此我需要创建一个 PTHREAD_MUTEX_ERRORCHECK 类型的互斥量,而我也无法创建一个。”? - Andrew Henle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接