Pthread Mutex блокирует разблокировку различными потоками

Наивный вопрос..

Я читаю, прежде чем говорить - " MUTEX должен быть разблокирован только потоком, который его заблокировал."

Но я написал программу, где THREAD1 блокирует mutexVar и идет на сон. Затем THREAD2 может непосредственно разблокировать mutexVar, выполнить некоторые операции и вернуться.

== > Я знаю, что все говорят, почему я так делаю? Но мой вопрос: это правильное поведение MUTEX??

== > Добавление примерного кода

void *functionC()
{
   pthread_mutex_lock( &mutex1 );
   counter++;
   sleep(10);
   printf("Thread01: Counter value: %d\n",counter);
   pthread_mutex_unlock( &mutex1 );
}

void *functionD()
{
   pthread_mutex_unlock( &mutex1 );
   pthread_mutex_lock( &mutex1 );
   counter=10;
   printf("Counter value: %d\n",counter);
}

int main()
{
   int rc1, rc2;
   pthread_t thread1, thread2;

   if(pthread_mutex_init(&mutex1, NULL))
   printf("Error while using pthread_mutex_init\n");

   if( (rc1=pthread_create( &thread1, NULL, &functionC, NULL)) )
   {   
      printf("Thread creation failed: %d\n", rc1);
   }   

   if( (rc2=pthread_create( &thread2, NULL, &functionD, NULL)) )
   {   
      printf("Thread creation failed: %d\n", rc2);
   } 

Ответ 1

То, что вы сделали, просто не является законным, а поведение - undefined. Мьютексы только исключают потоки, которые играют по правилам. Если вы попытались заблокировать mutex1 из потока 2, поток будет заблокирован, конечно; что нужно делать. В спецификации нет ничего, что говорит о том, что произойдет, если вы попытаетесь разблокировать мьютексы, которыми вы не владеете!

Ответ 2

Pthreads имеет 3 разных типа мьютексов: быстрый мьютекс, рекурсивный мьютекс и мьютекс с проверкой ошибок. Вы использовали быстрый мьютекс, который по соображениям производительности не проверяет эту ошибку. Если вы используете проверку ошибок mutex в Linux, вы обнаружите, что получаете ожидаемые результаты.

Ниже приведен небольшой пример вашей программы в качестве примера и доказательства. Он блокирует мьютексы в main(), и разблокировка в созданном потоке завершится с ошибкой.

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

/*** NOTE THE ATTR INITIALIZER HERE! ***/
pthread_mutex_t mutex1 = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;

int counter = 0;


void *functionD(void* data)
{
   int rc;

   if ((rc = pthread_mutex_unlock(&mutex1)) != 0)
   {
       errno = rc;
       perror("other thread unlock result");
       exit(1);
   }

   pthread_mutex_lock(&mutex1);
   counter=10;
   printf("Thread02: Counter value: %d\n",counter);

   return(data);
}


int main(int argc, char *argv[])
{
   int rc1;
   pthread_t thread1;

   if ((rc1 = pthread_mutex_lock(&mutex1)) != 0)
   {
       errno = rc1;
       perror("main lock result");
   }

   if( (rc1 = pthread_create(&thread1, NULL, &functionD, NULL)))
   {
      printf("Thread creation failed: %d\n", rc1);
   }

   pthread_join(thread1, NULL);
}

Ответ 3

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

Для этого мьютекс имеет несколько функций:

  • Мьютекс может обрабатывать условия гонки, связанные с несколькими потоками, пытаясь "заблокировать" мьютекс в одно и то же время и всегда приводит к тому, что один поток выигрывает гонку.

  • Любая нить, которая теряет гонку, засыпает постоянно, пока мьютекс не разблокируется. Мьютекс поддерживает список этих потоков.

  • A передаст "блокировку" одному и только одному из ожидающих потоков, когда мьютекс будет разблокирован потоком, который просто использовал его. Мьютекс разбудит эту нить.

Если этот тип шаблона полезен для какой-либо другой цели, тогда используйте его по другой причине.

Вернемся к вашему вопросу. Допустим, вы защищали некоторый код из нескольких потоков доступа с помощью мьютекса и разрешали говорить, что 5 потоков ожидали, когда поток A выполнял код. Если нить B (ни одна из тех, которые ждут с тех пор, как они постоянно спали в данный момент) разблокирует мьютекс, другой поток начнет выполнение кода одновременно с потоком A. Вероятно, не требуется.

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