Блокировать мьютекс несколько раз в одном потоке - PullRequest
10 голосов
/ 12 мая 2010

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

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

Ответы [ 5 ]

10 голосов
/ 12 мая 2010

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

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

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

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

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

ИСПРАВЛЕНИЕ:

Я не видел, чтобы вы устанавливали PTHREAD_MUTEX_RECURSIVE .... Позвольте мне подумать об этом еще немного.

ПОСЛЕ МЫШЛЕНИЯ:

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

2 голосов
/ 12 мая 2010

Звучит так, будто мьютекс 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
}
1 голос
/ 14 июня 2017

(только что понял, что не пометил этот вопрос как ответ)

Взято из выводов в вопросе:

  • Очевидно, что PTHREAD_MUTEX_RECURSIVE или PTHREAD_MUTEX_RECURSIVE_NP не работают, поэтому я не могу создать повторяющийся мьютекс.
  • try_lock тоже не годится. Он получает блокировку, если может, и возвращает ошибку, если не может получить блокировку. К сожалению, ошибка просто сообщает мне, что мьютекс уже используется, и я не могу узнать, владеет ли текущий поток блокировкой или нет.
  • pthread_mutex_lock может вернуть ошибку, если текущий поток имеет блокировку, но для этого мне нужно создать мьютекс типа PTHREAD_MUTEX_ERRORCHECK, и я тоже не могу его создать.
1 голос
/ 07 февраля 2017

Приведенный ниже код показывает, что нет проблем с блокировкой критической секции дважды, трижды или N раз перед вызовом разблокировки на pthread. Вы можете сделать несколько блокировок в одном потоке подряд перед разблокировкой, не беспокоясь, но учтите, это НЕ хорошая практика программиста. Правильный способ - это вызвать lock (), позволить потоку выполнить критическую секцию и вызвать unlock (), чтобы другие потоки могли выполнить один и тот же фрагмент кода между блокировкой и разблокировкой (называемая критической секцией). Приведенный ниже код предотвращает любые ошибки программиста, использующие ATTRIBUTES в 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

1 голос
/ 19 июня 2013

Вот рабочий код, протестированный на 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);

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

...