【问题标题】:Tricky Deadlock while programming with Threads in C在 C 中使用线程编程时出现棘手的死锁
【发布时间】:2015-05-19 14:19:37
【问题描述】:

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

//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;
}

【问题讨论】:

  • 您需要为回调函数使用正确的调用约定,see this。他们返回 int 而不是 void*。
  • functionRead(): char c; 应该是int c; 才能检测到EOF。请注意,fgetc() 返回 int,这是有意为之的。

标签: c multithreading pthreads mutex


【解决方案1】:

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

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

【讨论】:

  • 那么我应该为每个函数使用单独的互斥锁吗?
  • 鉴于您尝试在线程之间发送信号,一对信号量似乎要简单得多 - 一个读取和一个写入。将读取信号量初始化为 1,将写入信号量初始化为零。让读取线程循环等待读取信号量,读取数据,然后发布到写入信号量。写线程循环等待写信号量,写入数据,然后发布到读信号量。
  • @Kuro95:此外,由于您的线程是从一个线程到另一个线程的乒乓控制,因此实际上没有理由使用多个线程 - 您可以让一个线程执行读取,然后循环执行写入并避免同步单独线程的复杂性。我假设您这样做只是为了了解线程的工作原理?
  • 一个互斥体可以使用多个条件变量 - 不允许一个条件变量使用多个互斥体。
【解决方案2】:

您的主要问题是编写器线程的终止条件 - 当读取器线程决定退出时,它已经增加了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 &lt; 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() 返回int 的原因)。 c 也可以是 functionRead() 的本地地址。

  • 无需在主线程中发送condRead,因为读者会在等待之前测试condition

但也许最根本的是,我认为您可能错过了本练习的重点,因为尽管您有一个名为 ringpuffer 的数组,但您实际上并没有将它用作环形缓冲区。您没有获得任何并行性 - 您的设计意味着您的线程以锁步方式运行。我怀疑这个想法是你应该有两个独立的标志 - buffer_emptybuffer_full - 而不是一个单一的“阅读或写作”条件。只要buffer_full 不正确,您的文件阅读器就会运行,而只要buffer_empty 不正确,您的文件编写器就会运行,因此在两个标志都为假的部分时间内,您可以同时运行两个线程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-01
    • 2012-04-26
    相关资源
    最近更新 更多