如何使用多个互斥锁和条件控制pthread?

时间:2018-05-05 16:44:46

标签: c multithreading mutex

在下面的代码中,我编写了一个程序,使用多线程在int数组上执行添加/删除操作。条件是多个线程不能在同一个单元上进行操作,但可以在不同的单元上进行并行操作。

我认为为了实现这样的条件,我需要使用多个互斥锁和条件变量,确切地说,与阵列中的单元格一样多。我的数组的所有单元格的初始值为10,并且线程将此值递增/递减3

下面的代码似乎有用(所有线程完成工作后数组的单元格值都符合预期)但我不了解一些事情:

  1. 我首先产生了睡眠一秒钟的加法器线程。此外,每个线程都有printf语句,如果线程等待则会触发该语句。删除线程不要睡觉,所以我希望删除线程调用它们的printf语句,因为它们必须至少在加法器线程完成其工作之前等待一秒。 移除线程永远不会调用printf
  2. 我的第二个问题:正如我所提到的,我首先产生了加法器线程,所以我希望单元格值从10变为13.然后,如果移除线程获得锁定,则值可以从13到10 OR adder线程获取锁定,然后单元格值将从13到16但是我没有看到线程内printf语句中的行为。例如,我有一个printf序列:add thread id and cell id 1: cell value 10->13,然后是remove thread id and cell id 1: cell value 10->7,然后是add thread id and cell id 1: cell value 10->13。这没有意义。我确保线程都指向同一个数组。
  3. 底线我想知道我的解决方案是否正确,如果是,为什么我描述的行为发生了。如果我的解决方案不正确,我会理解正确解决方案或至少是一般方向的例子。

    这是代码(所有逻辑都在AdderThreadRemoveThread):

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <pthread.h>
    
    #define ARR_LEN 5
    #define THREADS_NUM 5
    #define INIT_VAL 10
    #define ADD_VAL 3
    #define REMOVE_VAL 3
    #define ADDER_LOOPS 2
    
    typedef struct helper_t {
        int threadId;
        int * arr;
        int * stateArr; //0 if free, 1 if busy
    } helper_t;
    
    enum STATE {FREE, BUSY};
    enum ERRORS {MUTEX, COND, CREATE, JOIN, LOCK, UNLOCK, WAIT, BROADCAST};
    
    pthread_mutex_t mutexArr[THREADS_NUM];
    pthread_cond_t condArr[THREADS_NUM];
    
    void errorHandler(int errorId) {
        switch (errorId) {
            case MUTEX:
                printf("mutex error\n");
                break;
            case COND:
                printf("cond error\n");
                break;
            case CREATE:
                printf("create error\n");
                break;
            case JOIN:
                printf("join error\n");
                break;
            case LOCK:
                printf("lock error\n");
                break;
            case UNLOCK:
                printf("unlock error\n");
                break;
            case WAIT:
                printf("wait error\n");
                break;
            case BROADCAST:
                printf("broadcast error\n");
                break;
            default:
                printf("default switch\n");
                break;
        }
    }
    
    void mallocError() {
        printf("malloc error\nExiting app\n");
        exit(EXIT_FAILURE);
    }
    
    void initMutexesAndConds(pthread_mutex_t * mutexArr, pthread_cond_t * condArr) {
        int i;
    
        for(i = 0; i < THREADS_NUM; i++) {
            pthread_mutex_init(&mutexArr[i], NULL);
            pthread_cond_init(&condArr[i], NULL);
        }
    }
    
    helper_t * initStructs(int * arr, int * stateArr) {
        int i;
        helper_t * helpers = (helper_t *) malloc(sizeof(helper_t) * THREADS_NUM);
        if(!helpers) {
            mallocError();
        } else {
            for(i = 0; i < THREADS_NUM; i++) {
                helpers[i].threadId = i;
                helpers[i].arr = arr;
                helpers[i].stateArr = stateArr;
            }
        }
        return helpers;
    }
    
    void printArr(int * arr, int len) {
        int i;
        for(i = 0; i < len; i++) {
            printf("%d, ", arr[i]);
        }
        printf("\n");
    }
    
    void * AdderThread(void * arg) {
        int i;
        helper_t * h = (helper_t *) arg;
        int id = h->threadId;
        for(i = 0; i < ADDER_LOOPS; i++) {
            pthread_mutex_t * mutex = &mutexArr[id];
            pthread_cond_t * cond = &condArr[id];
            if(pthread_mutex_lock(mutex)) {
                errorHandler(LOCK);
            }
            while(h->stateArr[id] == BUSY) {
                printf("adder id %d waiting...\n", id);
                if(pthread_cond_wait(cond, mutex)) {
                    errorHandler(WAIT);
                }
            }
            h->stateArr[id] = BUSY;
            sleep(1);
            h->arr[id] = h->arr[id] + ADD_VAL;
            printf("add thread id and cell id %d: cell value %d->%d\n", id, h->arr[id]-ADD_VAL, h->arr[id]);
            h->stateArr[id] = FREE;
            if(pthread_cond_broadcast(cond)) {
                errorHandler(BROADCAST);
            }
            if(pthread_mutex_unlock(mutex)) {
                errorHandler(UNLOCK);
            }
        }
        pthread_exit(NULL);
    }
    
    void * RemoveThread(void * arg) {
        helper_t * h = (helper_t *) arg;
        int id = h->threadId;
        pthread_mutex_t * mutex = &mutexArr[id];
        pthread_cond_t * cond = &condArr[id];
        if(pthread_mutex_lock(mutex)) {
            errorHandler(LOCK);
        }
        while(h->stateArr[id] == BUSY) {
            printf("remover id %d waiting...\n", id);
            if(pthread_cond_wait(cond, mutex)) {
                errorHandler(WAIT);
            }
        }
        h->stateArr[id] = BUSY;
        h->arr[id] = h->arr[id] - REMOVE_VAL;
        printf("remove thread id and cell id %d: cell value %d->%d\n", id, h->arr[id], h->arr[id]-ADD_VAL);
        h->stateArr[id] = FREE;
        if(pthread_cond_broadcast(cond)) {
            errorHandler(BROADCAST);
        }
        if(pthread_mutex_unlock(mutex)) {
            errorHandler(UNLOCK);
        }
        pthread_exit(NULL);
    }
    
    int main() {
        int i;
        helper_t * adderHelpers;
        helper_t * removeHelpers;
        pthread_t adders[THREADS_NUM];
        pthread_t removers[THREADS_NUM];
        int * arr = (int *) malloc(sizeof(int) * ARR_LEN);
        int * stateArr = (int *) malloc(sizeof(int) * ARR_LEN);
        if(!arr || !stateArr) {
            mallocError();
        }
    
        for(i = 0; i < ARR_LEN; i++) {
            arr[i] = INIT_VAL;
            stateArr[i] = FREE;
        }
    
        initMutexesAndConds(mutexArr, condArr);
        adderHelpers = initStructs(arr, stateArr);
        removeHelpers = initStructs(arr, stateArr);
    
        for(i = 0; i < THREADS_NUM; i++) {
            pthread_create(&adders[i], NULL, AdderThread, &adderHelpers[i]);
            pthread_create(&removers[i], NULL, RemoveThread, &removeHelpers[i]);
        }
    
        for(i = 0; i < THREADS_NUM; i++) {
            pthread_join(adders[i], NULL);
            pthread_join(removers[i], NULL);
        }
    
        printf("the results are:\n");
        printArr(arr, THREADS_NUM);
        printf("DONE.\n");
    
        return 0;
    }
    

1 个答案:

答案 0 :(得分:1)

1)这个代码序列在Addr中:

   h->stateArr[id] = BUSY;
        sleep(1);
        h->arr[id] = h->arr[id] + ADD_VAL;
        printf("add thread id and cell id %d: cell value %d->%d\n", id, h->arr[id]-ADD_VAL, h->arr[id]);
        h->stateArr[id] = FREE;

是否锁定互斥锁执行;因此,移除永远不会有机会将州视为免费的。

2)无法保证互斥拥有权交替(afaik),但至少,为了正确协调线程,您不应该依赖于这样的实现细节。工作与“恰好工作”之间的区别,通常会导致“习惯工作”......

如果你将sleep()置于互斥锁解锁和互斥锁之间,你可能会有一个更好的情况,但实际上它只是解锁它然后再锁定它,所以系统完全有权让它它继续执行。

[评论中我的空间用完了]:

是的,这里的条件变量对你没有任何作用。条件变量的想法是在某些共同异议发生重大事件(例如状态变化)时能够得到通知。

例如,水库可能具有水位的单一条件变量。多路复用到那可能是许多条件:等级&lt; 1米;等级&gt; 5米;等级&gt; 10米。为了保持系统独立(因此工作),更新级别的位可能只是:

pthread_mutex_lock(&levellock);
level = x;
pthread_cond_broadcast(&newlevel);
pthread_mutex_unlock(&levellock);

实施条件的演员会做类似的事情:

pthread_mutex_lock(&levellock);
while (1) {
    if (level is my conditions) {
         pthread_mutex_unlock(&levellock);
         alert the media
         pthread_mutex_lock(&levellock);
    }
    pthread_cond_wait(&newlevel, &levellock);
}

因此,我可以在不破坏级别设置代码或整个系统的情况下添加许多“状态监视器”。许多是有限的,但是当我向媒体发出警报时释放互斥锁,我避免让我的水监控系统依赖警报处理。

如果您熟悉“发布/订阅”,您可能会发现这很熟悉。这基本上是相同的模型,只是PS隐藏了一堆细节。