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

Я разрабатываю приложение для встроенной ОС 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, а я тоже не могу его создать.

person Megacan    schedule 12.05.2010    source источник
comment
Почему вы пытаетесь заблокировать его более одного раза? Обычно используются мьютексы, так что только одна часть кода может иметь блокировку в любой момент времени.   -  person WildCrustacean    schedule 12.05.2010
comment
В основном это связано с тем, как я настроил код и вспомогательные функции. Может быть, мне следует изменить свой код и избежать этого. В любом случае, мне любопытно.   -  person Megacan    schedule 12.05.2010
comment
Проверяете ли вы возвращаемое значение вызовов инициализации атрибутов и мьютексов? Может быть, ваша libc просто не поддерживает рекурсивные блокировки?   -  person Nikolai Fetissov    schedule 12.05.2010
comment
Вы пытаетесь использовать семафоры ( en.wikipedia.org/wiki/Semaphore_(programming%29, minek.com/files/unix_examples/semab.html )?   -  person Brian    schedule 12.05.2010
comment
@Nikolai: я забыл проверить возвраты, но я проверил их сейчас, и все они возвращают 0. @Brian: Нет, я не пытаюсь использовать семафоры.   -  person Megacan    schedule 13.05.2010


Ответы (5)


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

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

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

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

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

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

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

ПОСЛЕ ПОДУМА:

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

person Jeff B    schedule 12.05.2010
comment
Думаю, тогда мне придется использовать trylock. Я программировал в .Net последние два года и предполагал, что блокировки по умолчанию являются повторно входящими. Давно не программировал на С. - person Megacan; 13.05.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
}
person atk    schedule 12.05.2010

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

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

  • По-видимому, PTHREAD_MUTEX_RECURSIVE или PTHREAD_MUTEX_RECURSIVE_NP не работают, поэтому я не могу создать реентерабельный мьютекс.
  • try_lock тоже никуда не годится. Он получает блокировку, если может, и возвращает ошибку, если не может получить блокировку. К сожалению, ошибка просто сообщает мне, что мьютекс уже используется, и я не могу узнать, владеет ли уже текущий поток блокировкой или нет.
  • pthread_mutex_lock может возвращать ошибку, если текущий поток имеет блокировку, но для этого мне нужно создать мьютекс типа PTHREAD_MUTEX_ERRORCHECK, а я тоже не могу его создать.
person Megacan    schedule 14.06.2017

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

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

person Sam    schedule 19.06.2013

Приведенный ниже код показывает, что нет проблем с блокировкой критической секции дважды, трижды или 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

person Sammy    schedule 07.02.2017
comment
Вы прочитали часть вопроса, в которой говорится, что pthread_mutex_lock может возвращать ошибку, если текущий поток имеет блокировку, но для этого мне нужно создать мьютекс типа PTHREAD_MUTEX_ERRORCHECK, и я тоже не могу его создать.? - person Andrew Henle; 07.02.2017