简体   繁体   English

C eventcounter + sequencer中的死锁并发问题

[英]Deadlock concurrency issue in C eventcounter + sequencer

I'm trying to get this code to work, but for some reason it deadlocks within 30 seconds. 我正在尝试使此代码正常工作,但由于某种原因,它在30秒内陷入僵局。

The deadlock seems to happen Putting or Getting, whether the buffer is full not. 无论缓冲区是否已满,死锁似乎都发生在Puting或Getting中。

Am I missing something obvious or not using something right? 我是否缺少明显的东西或没有使用正确的东西? I'm new to C! 我是C的新手!

To compile: gcc -Wall -o test test.c -lpthread 要编译:gcc -Wall -o test test.c -lpthread

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>



struct sequence {
    pthread_mutex_t mutex;
    int count;
};

struct event {
    pthread_mutex_t critical;
    pthread_mutex_t critical2;
    pthread_mutex_t signalM;
    pthread_cond_t signalC;
    int eventCount;
};

struct allVars {
    struct sequence putSeq;
    struct sequence getSeq;
    struct event inEvents;
    struct event outEvents;
    int bufferSize;
    char buffer[10][128];
};



/**
 * Issue tickets in sequence. Used in conjunction with an Event Counter
 */
int getTicket(struct sequence *seq) {
    // begin critical section
    if (pthread_mutex_lock(&seq->mutex) != 0) {
        printf("mutex_lock in getticket error\n");
    }

    // remember current count
    int oldCount = seq->count;

    // increment count
    seq->count++;

    // end critical section
    if (pthread_mutex_unlock(&seq->mutex) != 0) {
        printf("mutex_unlock in getticket error\n");
    }

    return oldCount;
}



/**
 * Advance the EventCount (ticket)
 */
void advance(struct event *event) {
    // begin critical section
    if (pthread_mutex_lock(&event->critical) != 0) {
        fprintf(stderr, "mutex_lock in advance error\n");
        exit(EXIT_FAILURE);
    }

    // increment the event counter
    event->eventCount++;

    // end critical section
    if (pthread_mutex_unlock(&event->critical) != 0) {
        fprintf(stderr, "mutex_unlock in advance error\n");
        exit(EXIT_FAILURE);
    }

    // signal await to continue
    if (pthread_cond_signal(&event->signalC) != 0) {
        fprintf(stderr, "cond_signal in advance error\n");
        exit(EXIT_FAILURE);
    }
}



/**
 * Wait for ticket and buffer availability
 */
void await(struct event *event, int ticket) {

    int eventCount;

    // begin critical section
    if (pthread_mutex_lock(&event->critical) != 0) {
        fprintf(stderr, "mutex_lock in advance error\n");
        exit(EXIT_FAILURE);
    }

    eventCount = event->eventCount;

    // end critical section
    if (pthread_mutex_unlock(&event->critical) != 0) {
        fprintf(stderr, "mutex_unlock in advance error\n");
        exit(EXIT_FAILURE);
    }


    // loop until the ticket machine shows your number
    while (ticket > eventCount) {
        // wait until a ticket is called
        pthread_cond_wait(&event->signalC, &event->signalM);

        // begin critical section
        if (pthread_mutex_lock(&event->critical) != 0) {
            fprintf(stderr, "mutex_lock in advance error\n");
            exit(EXIT_FAILURE);
        }

        eventCount = event->eventCount;

        // end critical section
        if (pthread_mutex_unlock(&event->critical) != 0) {
            fprintf(stderr, "mutex_unlock in advance error\n");
            exit(EXIT_FAILURE);
        }
    }
}



/**
 * Add to buffer
 */
void putBuffer(struct allVars *allVars, char data[]) {
    // get a ticket
    int ticket = getTicket(&allVars->putSeq);

    // get the current write position
    int in;

    // begin critical section
    if (pthread_mutex_lock(&allVars->inEvents.critical) != 0) {
        fprintf(stderr, "mutex_lock in put error\n");
        exit(EXIT_FAILURE);
    }

    in = allVars->inEvents.eventCount;

    // end critical section
    if (pthread_mutex_unlock(&allVars->inEvents.critical) != 0) {
        fprintf(stderr, "mutex_unlock in put error\n");
        exit(EXIT_FAILURE);
    }

    // wait for ticket to be called (sequential writing)
    await(&allVars->inEvents, ticket);

    // wait until theres a space free in the buffer
    await(&allVars->outEvents, in - allVars->bufferSize + 1);   // set to 2 to keep 1 index distance

    // begin critical section
    if (pthread_mutex_lock(&allVars->inEvents.critical2) != 0) {
        fprintf(stderr, "mutex_lock in put error\n");
        exit(EXIT_FAILURE);
    }

    // add data to buffer
    strcpy(allVars->buffer[ticket % allVars->bufferSize], data);

    // end critical section
    if (pthread_mutex_unlock(&allVars->inEvents.critical2) != 0) {
        fprintf(stderr, "mutex_unlock in put error\n");
        exit(EXIT_FAILURE);
    }

    // increment the ticket display
    advance(&allVars->inEvents);
}



/**
 * Get from buffer
 */
char *getBuffer(struct allVars *allVars) {
    // get a ticket
    int ticket = getTicket(&allVars->getSeq);

    // get the current read position
    int out;

    // begin critical section
    if (pthread_mutex_lock(&allVars->outEvents.critical) != 0) {
        fprintf(stderr, "mutex_lock in get error\n");
        exit(EXIT_FAILURE);
    }

    out = allVars->outEvents.eventCount;

    // end critical section
    if (pthread_mutex_unlock(&allVars->outEvents.critical) != 0) {
        fprintf(stderr, "mutex_unlock in get error\n");
        exit(EXIT_FAILURE);
    }

    // wait for ticket to be called (sequential reading)
    await(&allVars->outEvents, ticket);

    // wait until theres something in the buffer
    await(&allVars->inEvents, out + 1);

    char *str = malloc(128);

    // critical section
    if (pthread_mutex_lock(&allVars->inEvents.critical2) != 0) {
        fprintf(stderr, "mutex_lock in put error\n");
        exit(EXIT_FAILURE);
    }

    // get the buffer data
    strcpy(str, allVars->buffer[ticket % allVars->bufferSize]);

    // end critical section
    if (pthread_mutex_unlock(&allVars->inEvents.critical2) != 0) {
        fprintf(stderr, "mutex_unlock in put error\n");
        exit(EXIT_FAILURE);
    }

    // increment buffer availability
    advance(&allVars->outEvents);

    return str;
}



/** child thread (producer) */
void *childThread(void *allVars) {
    char str[10];
    int count = 0;

    while (true) {
        sprintf(str, "%d", count++);
        putBuffer(allVars, str);
    }

    pthread_exit(EXIT_SUCCESS);
}



int main(void) {
    // init structs
    struct sequence putSeq = {
        PTHREAD_MUTEX_INITIALIZER,
        0
    };
    struct sequence getSeq = {
        PTHREAD_MUTEX_INITIALIZER,
        0
    };
    struct event inEvents = {
        PTHREAD_MUTEX_INITIALIZER,
        PTHREAD_MUTEX_INITIALIZER,
        PTHREAD_MUTEX_INITIALIZER,
        PTHREAD_COND_INITIALIZER,
        0
    };
    struct event outEvents = {
        PTHREAD_MUTEX_INITIALIZER,
        PTHREAD_MUTEX_INITIALIZER,
        PTHREAD_MUTEX_INITIALIZER,
        PTHREAD_COND_INITIALIZER,
        0
    };
    struct allVars allVars = {
        putSeq,         // sequence
        getSeq,
        inEvents,       // events
        outEvents,
        10,             // buffersize
        {"", {""}}      // buffer[][]
    };
    pthread_mutex_lock(&allVars.inEvents.signalM);
    pthread_mutex_lock(&allVars.outEvents.signalM);


    // create child thread (producer)
    pthread_t thread;
    if (pthread_create(&thread, NULL, childThread, &allVars)) {
        fprintf(stderr, "failed to create child thread");
        exit(EXIT_FAILURE);
    }


    // (consumer)
    while (true) {
        char *out = getBuffer(&allVars);
        printf("buf: %s\n", out);
        free(out);
    }


    return (EXIT_SUCCESS);
}

I am not sure if that's the source of your problems, but it is one error that sure can cause locking problems: 我不确定这是否是您的问题的根源,但这是肯定会导致锁定问题的一个错误:

You are using pthread_cond_wait and pthread_cond_signal wrong. 您使用的pthread_cond_waitpthread_cond_signal错误。 You should always lock the condition's mutex before calling pthread_cond_wait or pthread_cond_signal . 在调用pthread_cond_waitpthread_cond_signal之前,应始终锁定条件的互斥锁。

pthread_cond_wait automatically releases the mutex for you after you called it and reacquires the mutex after a signal. pthread_cond_wait调用后会自动为您释放互斥锁,并在发出信号后重新获取该互斥锁。 Therefore, you have to release the lock after signaling a thread. 因此,您必须在发出线程信号后释放锁。

This pthread programming tutorial helped me a lot when I was learning pthread programming for a class at my university. 当我在大学学习pthread编程课程时, 此pthread编程教程对我有很大帮助。 I recommend taking a look at it, since it also covers some other aspects of pthread programming (eg mutex). 我建议您看一下它,因为它也涵盖了pthread编程的其他方面(例如互斥体)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM