使用C语言编写线程时使用Tricky Deadlock进行编程

时间:2015-05-19 14:19:38

标签: c multithreading pthreads mutex

我尝试使用线程制作文件复制器,不知何故,程序在进入函数时锁定。我搜索了很多,我尝试了很多东西,但我根本找不到解决方案。如果有人可以帮助我,我会很高兴的!

//gcc -o threadcopyfile threadcopyfile.c -lpthread -lrt
#include <stdio.h>
#include <pthread.h> 
#include <unistd.h> 
#include <string.h>
#include <stdlib.h>


pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //init mutex
pthread_cond_t condRead = PTHREAD_COND_INITIALIZER; //init cond. variables
pthread_cond_t condWrite = PTHREAD_COND_INITIALIZER; //init cond. variables

int n, x = 1, i = 0, j, condition = 0;
char c;

pthread_t pRead;
pthread_t pWrite;

char ringpuffer[10000];

void *functionRead(void *argv){
    char **args = (char **) argv; //Give args the arguments from argv
    FILE* fp;
    if ((fp=fopen(args[1], "r")) == NULL){ //Open the inputfile, check if failed
        perror("fopen\n"); 
        exit(EXIT_FAILURE);
    }
    n = atoi(args[3]);
    printf("Entered functionRead\n");
    do{
        pthread_mutex_lock(&mutex); //Lock mutex
        while(condition == 1){
            pthread_cond_wait(&condRead, &mutex); //Wait for cond_signal
        }
        i = 0;
        for(i = 0; i<=n; i++){ //Put chars into the ringbuffer
            c = fgetc(fp);
            ringpuffer[i] = c;
            i++;
        }
        x++;
        printf("Hit!");
        condition = 1;
        pthread_mutex_unlock(&mutex); //Unlock mutex
        pthread_cond_signal(&condWrite); //Send signal to CondWrite
    }while ((c=fgetc(fp)) != EOF);

    if ((fclose(fp)) == EOF){ //Close the file
        perror("fclose\n"); 
        exit(EXIT_FAILURE);
    }
    return 0;
}

void *functionWrite(void *argv){
    char **args = (char **) argv;   //Give args the arguments from argv
    FILE* fp;
    if ((fp=fopen(args[2], "w")) == NULL){ //Open the outputfile, check if allowed
        perror("fopen\n"); 
        exit(EXIT_FAILURE);
    }
    n = atoi(args[3]);
    printf("Entered functionWrite\n");
    for (j = 0; j < x; j++){
        pthread_mutex_lock(&mutex); //Lock mutex
        while(condition == 0){
            pthread_cond_wait(&condWrite, &mutex); //Wait for singel from condRead
        }
        fwrite(ringpuffer, 1, n, fp); //Write to file
        printf("Hit too!");
        condition = 0;
        pthread_mutex_unlock(&mutex); //Unlock
        pthread_cond_signal(&condRead); //Send signal again to condRead
    }

    if ((fclose(fp)) == EOF){
        perror("fclose\n"); 
        exit(EXIT_FAILURE);
    }
    return EXIT_SUCCESS;
}

int main(int argc, char **argv)
{
    if (argc != 4){
        printf("Only 3 parameters allowed!\n");
        exit(EXIT_FAILURE);
    }
    printf("File to Copy: %s\nTarget: %s\nHow many chars: %s\n", argv[1], argv[2], argv[3]);

    pthread_create(&pWrite, NULL, &functionWrite, ((void *)argv)); //Create thread, threadid in pWrite, execute functionWrite, and give argv as arguments
    pthread_create(&pRead, NULL, &functionRead, ((void *)argv)); //Create thread, threadid in pRead, execute functionRead and give argv as arguments 

    printf("Threads created, file copy in progress\n");
    pthread_cond_signal(&condRead); //Unblocks the thread

    pthread_join(pRead, NULL);
    pthread_join(pWrite, NULL);
    printf("done.\n");
    return 0;
}

2 个答案:

答案 0 :(得分:2)

您不能使用具有两个条件变量的单个互斥锁。如果一个线程调用pthread_cond_signal()并在第二个线程唤醒之前重新锁定互斥锁,则第二个线程永远无法从pthread_cond_wait()返回,因为它无法锁定互斥锁。

您似乎也没有设置condition = 1,因为functionRead()functionWrite()中都有condition = 0

答案 1 :(得分:0)

你的主要问题是你的编写器线程的终止条件 - 当读者线程决定退出时,它已经增加x,因此编写器线程将等待另一次...永远。

你也不应该在while循环的终止条件下再次调用c = fgetc(fp),因为你最终会抛弃那里读取的字符。相反,您读取缓冲区的循环可能是:

i = 0;
while (i < n && (c = fgetc(fp)) != EOF) { //Put chars into the ringbuffer
    ringpuffer[i] = c;
    i++;
}

在这个循环结束时,我们知道:

  • 我们准确地将i个字符读入缓冲区;
  • 如果i < n,我们已到达文件结尾并应退出。

您可以使用其中第二个点来了解读者线程何时完成,因此整个循环如下所示:

do {
    pthread_mutex_lock(&mutex); //Lock mutex
    while(condition == 1){
        pthread_cond_wait(&condRead, &mutex); //Wait for cond_signal
    }
    i = 0;
    while (i < n && (c = fgetc(fp)) != EOF) { //Put chars into the ringbuffer
        ringpuffer[i] = c;
        i++;
    }
    printf("Hit!");
    condition = 1;
    pthread_mutex_unlock(&mutex); //Unlock mutex
    pthread_cond_signal(&condWrite); //Send signal to CondWrite
} while (i == n);

要解决写入程序线程终止的实际问题,可以使用相同的终止条件。但是,我们需要在编写器线程中测试i时保持锁定,否则它可能会在读取器线程中写入i。要做到这一点,我们将解锁和锁定移到循环外部(这没关系,因为线程没有持有锁定的唯一时间长度是pthread_cond_wait()无论如何)。我们还使用i作为从缓冲区写入的字节数,因为读者线程离开该值的位置是:

pthread_mutex_lock(&mutex); //Lock mutex
printf("Entered functionWrite\n");
do {
    while(condition == 0){
        pthread_cond_wait(&condWrite, &mutex); //Wait for singel from condRead
    }
    fwrite(ringpuffer, 1, i, fp); //Write to file
    printf("Hit too!");
    condition = 0;
    pthread_cond_signal(&condRead); //Send signal again to condRead
} while (i == n);
pthread_mutex_unlock(&mutex); //Unlock

完成这些更改后,不再需要jx

您还可以看到其他几个问题:

  • n由作者和读者线程设置,没有锁定。你可以在读者线程中设置它,因为在读者线程执行一次之前,编写者线程不会访问它。

  • 变量c应该是int,而不是char。否则,c可能永远不会与EOF进行比较,因为EOF不一定在char范围内(这就是为什么fgetc()返回{{ 1}})。 int也可以是c的本地人。

  • 没有必要在主线程中发出functionRead()信号,因为读者会在等待前测试condRead

但也许最根本的是,我认为你可能已经错过了这个练习的重点,因为虽然你有一个名为condition的数组,但你实际上并没有将它用作环形缓冲区。您没有获得任何并行性 - 您的设计意味着您的线程以锁定步骤运行。我怀疑这个想法是你应该有两个单独的标志 - ringpufferbuffer_empty - 而不是单个&#34;读或写&#34;条件。只要buffer_full不正确,您的文件阅读器就会运行,并且只要buffer_full为真,您的文件编写器就会运行,因此对于两个标志都为假的部分时间你可以让两个线程都运行。