Блокировка мьютекса несколько раз в одном потоке

Я разрабатываю приложение на встроенной ОС linux (uClinux), и мне нужно иметь возможность блокировать мьютекс более одного раза (тем же потоком).

у меня есть мьютекс и мьютексаттр, определенные и инициализированные следующим образом:

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, и я не могу его создать.

5 ответов


разве это не делает то, что вы ожидаете?

первый вызов получает блокировку, а второй блокируется до тех пор, пока не будет освобождена первая блокировка (pthread_mutex_unlock). Вот что делают замки.

из документации:

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

возможно, вы хотите pthread_mutex_trylock? Трудно сказать, если мы не знаем, что вы пытаетесь выполнять.

устранение:

Я не видел, что вы устанавливаете PTHREAD_MUTEX_RECURSIVE.... Дай мне еще немного подумать.

ПОДУМАВ:

от ковыряния вокруг Google codesearch, похоже, что PTHREAD_MUTEX_RECURSIVE не реализован во всех библиотеках. Вы можете попробовать PTHREAD_MUTEX_RECURSIVE_NP, или вы можете сделать что-то необычное, чтобы обойти это.


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

bool haveLock = false;// thread variable
pthread_mutex_t waiting_barcode_mutex; // also thread var

mylock()
{
   if( haveLock ) return; // no need to lock twice
   pthread_mutex_lock(&waiting_barcode_mutex);
   haveLock = true;
}

myunlock()
{
   haveLock = false;
   pthread_mutex_unlock(&waiting_barcode_mutex); // or whatever the unlock call is
}

вот рабочий код, протестированный на UBUNTU 12.04 LTS на моем Dell m6300:

  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);

Не забудьте освободить мьютекс столько раз, сколько вы его приобрели.


приведенный ниже код показывает, что нет проблем с блокировкой критического раздела дважды или трижды или N раз перед вызовом разблокировки на pthread. Вы можете сделать несколько блокировок в одном потоке последовательно перед разблокировкой, не беспокоясь, но имейте в виду, это не хорошая практика программиста. Правильный способ-вызвать lock (), позволить потоку выполнить критический раздел и вызвать unlock (), чтобы другие потоки могли выполнить тот же фрагмент кода между lock и unlock (называемый, критическая секция.) Приведенный ниже код предотвращает ошибки программиста с использованием атрибутов в pthread).

читать!

// 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_RECURSIVE или PTHREAD_MUTEX_RECURSIVE_NP не работают, поэтому я не могу создать реентрантный мьютекс.
  • try_lock тоже не годится. Он получает блокировку, если может, и возвращает ошибку, если не может получить блокировку. К сожалению, ошибка просто сообщает мне, что мьютекс уже используется, и я не могу узнать, текущий поток уже владеет блокировкой или нет.
  • pthread_mutex_lock может вернуть ошибку, если текущий поток имеет блокировку, но для этого мне нужно создать мьютекс типа PTHREAD_MUTEX_ERRORCHECK, и я не могу его создать.