递归互斥锁背后的想法

时间:2014-03-25 16:26:09

标签: c++ c multithreading mutex recursive-mutex

我正在学校实验室工作,我们被指示为计数程序创建一个递归互斥锁。我已经编写了一些代码(它不起作用),但我认为这主要是因为我不理解使用递归互斥锁的真正想法。任何人都可以详细说明递归互斥锁应该做什么/看起来像什么?

一般注意:我没有要求答案,只是澄清了递归互斥锁应该做什么。

此外,如果有人好奇,这里是必需的代码。我正在编辑/实现的代码是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;
}

2 个答案:

答案 0 :(得分:10)

递归互斥锁的想法是它可以被当前持有锁的线程成功重新锁定。例如:

如果我有这样的互斥量(这是伪代码):

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已将其锁定,如果它已被锁定,并且它是否与当前线程ID匹配,则返回成功。

答案 1 :(得分:3)

递归互斥体的意思是让你写下这个:

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()调用它。如果你使用普通的互斥锁(),那么当从bar()调用foo()时,线程会自死锁,因为普通的互斥锁()函数在互斥锁被解锁之前不会返回,并且没有别的将解锁它的线程。

你的recursive_mutex_lock()需要区分这些情况; (1)互斥锁未锁定,(2)互斥锁已被锁定,但调用线程是所有者,(3)互斥锁已被某个其他线程锁定。

Case(3)需要阻止调用线程,直到所有者完全解锁互斥锁。此时,它转换为case(1)。这里有一个提示:使用条件变量处理case(3)。也就是说,当调用线程不是所有者时,调用线程应该执行pthread_condition_wait(...)调用。