Идея позади рекурсивного замка мьютекса

Я работаю в школьной лаборатории, и нам поручено создать рекурсивную блокировку мьютекса для программы подсчета. Я написал некоторый код (который не работает), но я думаю, что это в основном потому, что я не понимаю реальной идеи использования рекурсивной блокировки мьютекса. Может ли кто-нибудь объяснить, как должна выглядеть / выглядеть рекурсивная блокировка мьютекса?

Общее примечание: я не прошу ответа, просто поясню, что должна делать рекурсивная блокировка мьютекса.

Также, если кому-то интересно, вот код, необходимый для этого. Код, который я редактирую / реализую, — это recmutex.c.

recmutex.h

#include <pthread.h>

/*
* The recursive_mutex structure.
*/

struct recursive_mutex {

pthread_cond_t    cond;
pthread_mutex_t   mutex; //a non-recursive pthread mutex
pthread_t         owner;
unsigned int      count;
unsigned int      wait_count;
};

typedef struct recursive_mutex  recursive_mutex_t;/* Initialize the recursive mutex object.
*Return a non-zero integer if errors occur.
*/

int recursive_mutex_init (recursive_mutex_t *mu);/* Destroy the recursive mutex object.
*Return a non-zero integer if errors occur.
*/

int recursive_mutex_destroy (recursive_mutex_t *mu);/* The recursive mutex object referenced by mu shall be
locked by calling pthread_mutex_lock(). When a thread
successfully acquires a mutex for the first time,
the lock count shall be set to one and successfully return.
Every time a thread relocks this mutex, the lock count
shall be incremented by one and return success immediately.
And any other calling thread can only wait on the conditional
variable until being waked up. Return a non-zero integer if errors occur.
*/
int recursive_mutex_lock (recursive_mutex_t *mu);/* The recursive_mutex_unlock() function shall release the
recursive mutex object referenced by mu. Each time the owner
thread unlocks the mutex, the lock count shall be decremented by one.
When the lock count reaches zero, the mutex shall become available
for other threads to acquire. If a thread attempts to unlock a
mutex that it has not locked or a mutex which is unlocked,
an error shall be returned. Return a non-zero integer if errors occur.
*/

int recursive_mutex_unlock (recursive_mutex_t *mu);

recmutex.c: содержит функции для рекурсивного мьютекса

#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include "recmutex.h"
int recursive_mutex_init (recursive_mutex_t *mu){
int err;
err = pthread_mutex_init(&mu->mutex, NULL);
if(err != 0){
perror("pthread_mutex_init");
return -1;
}else{
return 0;
}
return 0;
}

int recursive_mutex_destroy (recursive_mutex_t *mu){
int err;
err = pthread_mutex_destroy(&mu->mutex);
if(err != 0){
perror("pthread_mutex_destroy");
return -1;
}else{
return 1;
}
return 0;
}

int recursive_mutex_lock (recursive_mutex_t *mu){

if(mutex_lock_count == 0){
pthread_mutex_lock(&mu->mutex);
mu->count++;
mu->owner = pthread_self();
printf("%s", mu->owner);
return 0;
}else if(mutex_lock_count > 0){
pthread_mutex_lock(&mu->mutex);
mu->count++;
mu->owner = pthread_self();
return 0;
}else{
perror("Counter decremented incorrectly");
return -1;
}
}

int recursive_mutex_unlock (recursive_mutex_t *mu){

if(mutex_lock_count <= 0){
printf("Nothing to unlock");
return -1;
}else{
mutex_lock_count--;
pthread_mutex_unlock(&mu->mutex);
return 0;
}
}

count_recursive.cc: Программа подсчета, упомянутая выше. Использует функции recmutex.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include "recmutex.h"
//argument structure for the thread
typedef struct _arg_{
int n1;
int n2;
int ntimes;
}Arg;

int count; //global counter
recursive_mutex_t mutex; //the recursive mutex

void do_inc(int n){
int ret;
if(n == 0){
return;
}else{
int c;
ret = recursive_mutex_lock(&mutex);
assert(ret == 0);
c = count;
c = c + 1;
count = c;
do_inc(n - 1);
ret = recursive_mutex_unlock(&mutex);
assert(ret == 0);
}
}

/*  Counter increment function. It will increase the counter by n1 * n2 * ntimes. */
void inc(void *arg){
Arg * a = (Arg *)arg;
for(int i = 0; i < a->n1; i++){
for(int j = 0; j < a->n2; j++){
do_inc(a->ntimes);
}
}
}

int isPositiveInteger (const char * s)
{
if (s == NULL || *s == '\0' || isspace(*s))
return 0;
char * p;
int ret = strtol (s, &p, 10);
if(*p == '\0' && ret > 0)
return 1;
else
return 0;
}

int test1(char **argv){

printf("==========================Test 1===========================\n");
int ret;
//Get the arguments from the command line.
int num_threads = atoi(argv[1]); //The number of threads to be created.
int n1 = atoi(argv[2]);          //The outer loop count of the inc function.
int n2 = atoi(argv[3]);          //The inner loop count of the inc function.
int ntimes = atoi(argv[4]);      //The number of increments to be performed in the do_inc function.

pthread_t *th_pool = new pthread_t[num_threads];
pthread_attr_t attr;
pthread_attr_init( &attr );
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);

ret = recursive_mutex_init(&mutex);
assert(ret == 0);

printf("Start Test. Final count should be %d\n", num_threads * n1 * n2 * ntimes );

// Create threads
for(int i = 0; i < num_threads; i++){
Arg *arg = (Arg *)malloc(sizeof(Arg));
arg->n1 = n1;
arg->n2 = n2;
arg->ntimes = ntimes;
ret = pthread_create(&(th_pool[i]), &attr, (void * (*)(void *)) inc, (void *)arg);
assert(ret == 0);
}

// Wait until threads are done
for(int i = 0; i < num_threads; i++){
ret = pthread_join(th_pool[i], NULL);
assert(ret == 0);
}

if ( count != num_threads * n1 * n2 * ntimes) {
printf("\n****** Error. Final count is %d\n", count );
printf("****** It should be %d\n", num_threads * n1 * n2 * ntimes );
}
else {
printf("\n>>>>>> O.K. Final count is %d\n", count );
}

ret = recursive_mutex_destroy(&mutex);
assert(ret == 0);

delete [] th_pool;
return 0;
}int foo(){
int ret;
printf("Function foo\n");
ret = recursive_mutex_unlock(&mutex);
assert(ret != 0);
return ret;
}

//test a thread call unlock without actually holding it.
int test2(){
int ret;
printf("\n==========================Test 2==========================\n");
pthread_t th;
pthread_attr_t attr;
pthread_attr_init( &attr );
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);

ret = recursive_mutex_init(&mutex);
ret = pthread_create(&th, &attr, (void * (*)(void *))foo, NULL);

printf("Waiting for thread to finish\n");
ret = pthread_join(th, NULL);
assert(ret == 0);
return 0;
}int main( int argc, char ** argv )
{
int ret;
count = 0;

if( argc != 5 ) {
printf("You must enter 4 arguments. \nUsage: ./count_recursive num_threads n1 n2 ntimes\n");
return -1;
}

if(isPositiveInteger(argv[1]) != 1 || isPositiveInteger(argv[2]) != 1 || isPositiveInteger(argv[3]) != 1 || isPositiveInteger(argv[4]) != 1 ){
printf("All the 4 arguments must be positive integers\n");
return -1;
}

test1(argv);

test2();

return 0;
}

4

Решение

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

если бы у меня было несколько мьютексов вроде этого (это псевдокод):

mutex l;
recursive_mutex r;

В одной теме, если я сделал это:

l.lock();
l.lock(); // this would hang the thread.

но

r.lock();
r.lock();
r.lock(); // this would all pass though with no issue.

При реализации рекурсивного мьютекса вам нужно проверить, какой threadId заблокировал его, если он был заблокирован, и если он совпадает с текущим идентификатором потока, верните успех.

10

Другие решения

Суть рекурсивного мьютекса в том, чтобы позволить вам написать это:

recursive_mutext_t rmutex;

void foo(...) {
recursive_lock_lock(&rmutex);
...
recursive_lock_unlock(&rmutex);
}

void bar(...) {
recursive_lock_lock(&rmutex);
...
foo(...);
...
recursive_lock_unlock(&rmutex);
}

void baz(...) {
...
foo(...);
...
}

Функция foo () нуждается в блокировке мьютекса, но вы хотите иметь возможность вызывать его либо из bar (), где тот же мьютекс уже заблокирован, либо из baz (), где мьютекс не заблокирован. Если вы использовали обычный mutex (), поток самоблокируется при вызове foo () из bar (), потому что обычная функция mutex lock () не вернется, пока мьютекс не будет разблокирован, и нет другого потока, который разблокирует Это.

Ваш recursive_mutex_lock () должен различать эти случаи; (1) мьютекс не заблокирован, (2) мьютекс уже заблокирован, но вызывающий поток является владельцем, и (3) мьютекс уже заблокирован каким-либо другим потоком.

Случай (3) должен блокировать вызывающий поток, пока владелец полностью не разблокирует мьютекс. В этот момент он преобразуется в случай (1). Вот подсказка: обрабатывать case (3) с помощью условной переменной. То есть, когда вызывающий поток не является владельцем, вызывающий поток должен выполнить вызов pthread_condition_wait (…).

3

По вопросам рекламы [email protected]