Подтвердить что ты не робот

Если вы разблокируете уже разблокированный мьютекс, это поведение undefined?

Если вы разблокируете уже разблокированный мьютекс, это поведение небезопасно, безопасно или undefined?

Цель вопроса связана с следующим кодом, где я не знаю, было бы лучше разблокировать мьютексы в блоке if или просто вне блока if.

    // This chunk of code makes dual locking semi-autonomous.
    int c_lckd = 0, q_lckd = 0;
    if (pthread_mutex_trylock(&crunch_mutex) == 0) c_lckd = 1;
    if (pthread_mutex_trylock(&queue_mutex) == 0) q_lckd = 1;
    if (q_lckd && !c_lckd) { QUEUE_UNLOCK; q_lckd = 0; }
    else if (c_lckd && !q_lckd) { CRUNCH_UNLOCK; c_lckd = 0; }

    if (c_lckd && q_lckd) {
      printf("cr = %d, max = %d, cnt = %d\n",
        crunching, max_crunching, queue_count(conn_queue));
      if (crunching < max_crunching && queue_count(conn_queue)) {
        pthread_t tid =
          pthread_create(
            &tid,
            NULL,
            crunch_conn,
            (void *)queue_dequeue(conn_queue)
          );
        crunching++;
      }
      CRUNCH_UNLOCK QUEUE_UNLOCK
    }

Спасибо, Chenz

4b9b3361

Ответ 1

Для pthreads это приведет к поведению undefined. На странице man для pthread_mutex_unlock:

Вызов pthread_mutex_unlock() с мьютексом, который не поддерживает вызывающий поток, приведет к поведению undefined.

Другие мьютексы будут иметь свое собственное поведение. Как утверждали другие, лучше всего прочитать руководство по использованию любого мьютекса, который вы используете.

Ответ 2

Как отметил Глен, вы получаете поведение undefined, если вы пытаетесь разблокировать разблокированный мьютекс - не пытайтесь его использовать. Отладка потоков достаточно сложна, не вызывая поведение undefined.

Что еще более важно, стиль кодирования немного необычен - поскольку вы ничего не собираетесь делать, если не получите оба замка, код соответственно:

if (pthread_mutex_trylock(&crunch_mutex) == 0)
{
    if (pthread_mutex_trylock(&queue_mutex) == 0)
    {
        printf("cr = %d, max = %d, cnt = %d\n",
               crunching, max_crunching, queue_count(conn_queue));
        if (crunching < max_crunching && queue_count(conn_queue))
        {
            pthread_t tid;
            int rc = pthread_create(&tid, NULL,
                               crunch_conn, (void *)queue_dequeue(conn_queue));
            if (rc != 0)
            {
                // Error recovery
                // Did you need what was returned by queue_dequeue()
                // to requeue it, perhaps?
            }
            else
            {
                crunching++;
                // Do something with tid here?
            }
        }
        QUEUE_UNLOCK;
    }
    CRUNCH_UNLOCK;
}

Это позволяет избежать переменных "я сделал это"; также ясно, что до тех пор, пока макросы разблокировки выполняют то, что ожидается (и нет никаких случайных исключений или setjmps вокруг), что блокируемые блокировки мьютексов разблокируются. Это также позволяет избежать потери энергии при блокировке мьютекса очереди, когда хьюч-хьютекс недоступен, но это второстепенная проблема по сравнению с добавленной ясностью.

Ответ 3

В целом, для таких вопросов документация является лучшим источником информации. Различные мьютексы могут вести себя по-другому или могут быть варианты одного мьютекса, которые заставляют его вести себя иначе (например, в случае рекурсивного получения мьютекса в одном потоке).

Ответ 4

Вам не нужно так поступать. Попробуйте следующее:

    // This chunk of code makes dual locking semi-autonomous.
int c_lckd = 0, q_lckd = 0;
if (pthread_mutex_trylock(&crunch_mutex) == 0) c_lckd = 1;
if (pthread_mutex_trylock(&queue_mutex) == 0) q_lckd = 1;

if (c_lckd && q_lckd) {
  printf("cr = %d, max = %d, cnt = %d\n",
    crunching, max_crunching, queue_count(conn_queue));
  if (crunching < max_crunching && queue_count(conn_queue)) {
    pthread_t tid =
      pthread_create(
        &tid,
        NULL,
        crunch_conn,
        (void *)queue_dequeue(conn_queue)
      );
    crunching++;
  }

}

if (q_lckd) { QUEUE_UNLOCK; q_lckd = 0; }
if (c_lckd) { CRUNCH_UNLOCK; c_lckd = 0; }

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

Ответ 5

Разблокировка мьютекса должна выполняться в потоке только в том случае, если один и тот же мьютекс заблокирован ранее в том же потоке. Все остальные случаи undefined behviour в соответствии с man-страницей.

Если тип мьютекса - PTHREAD_MUTEX_DEFAULT, попытка рекурсивно блокировка результатов мьютекса в режиме undefined. Попытка разблокировать мьютекс, если он не был заблокирован вызывающей нитью, undefined. Попытка разблокировать мьютекс, если это не заблокированные результаты в undefined.

Ответ 6

Попробуйте. Это правильный код.

// Mutex is not busy
if(pthread_mutex_trylock(&object->mtx) == 0) {
    if(pthread_mutex_unlock(&object->mtx)!=0) {
        perror("ERRN: pthread_mutex_unlock:");
    }
}
// Mutex is already busy
else {
    if(pthread_mutex_unlock(&object->mtx)!=0) {
        perror("ERRN: pthread_mutex_unlock:");
    }
}

// В этом пункте - мы правильно разблокировали мьютекс.

if(pthread_mutex_destroy(&object->mtx) != 0) {
    perror("ERRN: pthread_mutex_destroy:");
}