pthreads - pthread_cond_wait上的死锁

时间:2018-04-26 20:28:43

标签: c linux multithreading pthreads deadlock

我试图学习如何在c中使用线程,我制作了一个计算文件中行数的程序。该程序为每个文件创建一个线程,以便可以同时计算不同的文件。 我遇到的问题是程序阻塞,这让我觉得发生了死锁。我希望有人可以解释为什么会发生这种情况。测试运行涉及10个文本文件。我也在下面的代码中省略了错误检查。

#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
struct thefiles * mailbox;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t flag =  PTHREAD_COND_INITIALIZER;
struct thefiles {
  char    *name;
  int linecount;
};
void *count_words(void *a) {
  char    *buffer;
  int          fd;
  size_t    total;
  int   count = 0;
  int    size = 0;
  struct thefiles *fls = malloc(sizeof(*fls));

  fd = open(a, O_RDONLY);
  size   = lseek(fd, 0, SEEK_END);
  buffer =  malloc(size);
  lseek(fd, 0, SEEK_SET);
  read(fd, buffer, size);
  while (*buffer) {
    if ( *buffer == '\n' )
      count++;
    buffer++;
        }
  fls->name = a;
  fls->linecount = count;
  pthread_mutex_lock(&lock);
  if (mailbox != NULL)
    pthread_cond_wait(&flag, &lock);
  mailbox = fls;
  pthread_cond_signal(&flag);
  pthread_mutex_unlock(&lock);
}

int main(int argc, char *argv[argc]) {
  int                i;
  int        total = 0;
  pthread_t   *threads;
  struct thefiles *fls;
  threads = malloc(sizeof(pthread_t)*(--argc));
  for (i = 0; i < argc; i++) {
    pthread_create(&threads[i], NULL, count_words, argv[i+1]);
  }
  pthread_mutex_lock(&lock);
  while (argc) {
    pthread_cond_wait(&flag, &lock);
    total += mailbox->linecount;
    free(mailbox);
    mailbox = NULL;
    pthread_cond_signal(&flag);
    argc--;
  }
  printf("%d\n", total);
  for (i = 0; i < argc; i++) {
    pthread_join(threads[i], NULL);
  }
}

2 个答案:

答案 0 :(得分:2)

您至少遇到以下问题:

  • main()的循环中,mailbox返回时,不检查pthread_cond_wait()是否为NULL。函数pthread_cond_wait()可以随时返回,即使未调用pthread_cond_signal(),因此您需要考虑发生这种情况的情况。

  • 同样,在count_words()中,您不会检查mailbox是否已变为NULL。应始终在pthread_cond_wait()循环中调用while,方法如下:

      while (!condition) {
          pthread_cond_wait()
      }
    

另外,我建议您检查错误代码 - 即使您只是打印错误并退出,它比盯着没有做任何事情的程序更有教育意义:)

答案 1 :(得分:1)

你无缘无故地复杂化了,你会很难过 当你自己把石头放在路上的时候想出穿线。使用的理由 条件变量用于何时必须同步线程的时间 线程只能继续工作,当另一个已达到某种状态并且两个(或更多线程)必须时 同步。但是,在这种情况下,没有线程依赖于其他线程 在做某事之前,主线程可以愉快地等待所有工人结束 是有用的。

psmears指出了代码中的一些问题。我想继续前进 回答并向您展示如何使用线程来解决此问题 吞吐量太复杂(不需要信号,不需要互斥)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>

struct thefiles {
  char *name;
  int linecount;
  int fo_errno;
};

void *count_lines(void *args)
{
    struct thefiles *data = args;

    FILE *fp = fopen(data->name, "r");

    if(fp == NULL)
    {
        data->linecount = -1; // signaling error
        data->fo_errno = errno;
        pthread_exit(0);
    }

    data->linecount = 0;

    char line[1024];
    char *nl;
    while(fgets(line, sizeof line, fp))
    {
        nl = strchr(line, '\n');

        if(nl)
            data->linecount++;
    }

    // last line of file did not end in newline
    if(nl == NULL)
        data->linecount++;

    fclose(fp);

    pthread_exit(0);
}

int main(int argc, char **argv)
{
    if(argc < 2)
    {
        fprintf(stderr, "usage: %s file [file ...]\n", argv[0]);
        return 1;
    }

    struct thefiles *fls = calloc(argc - 1, sizeof *fls);
    if(fls == NULL)
    {
        fprintf(stderr, "Not enough memory\n");
        return 1;
    }

    pthread_t *ths = calloc(argc - 1, sizeof *ths);

    if(ths == NULL)
    {
        free(fls);
        fprintf(stderr, "Not enough memory\n");
        return 1;
    }

    for(int i = 0; i < argc - 1; ++i)
    {
        fls[i].name = argv[i+1];

        pthread_create(ths + i, NULL, count_lines, fls + i);
    }

    int total = 0;
    for(int i = 0; i < argc - 1; ++i)
    {
        pthread_join(ths[i], NULL);
        if(fls[i].linecount == -1)
        {
            fprintf(stderr, "%s: %s: %s\n", argv[0], fls[i].name, strerror(fls[i].fo_errno));
            continue;
        }

        printf("%4d %s\n", fls[i].linecount, fls[i].name);
        total += fls[i].linecount;
    }

    printf("%4d total\n", total);

    free(ths);
    free(fls);

    return 0;
}

如你所见,它比你的简单得多,不需要信号和 条件变量等,工人完成一件事:打开 file,count the lines,将结果存储在传递给线程的参数中 并退出。主线程创建线程并连接它们并打印出来 结果:

$ ./b /etc/fstab a.c b.c aa
  46 /etc/fstab
 124 a.c
  98 b.c
./b: aa: No such file or directory
 268 total

$ wc -l /etc/fstab a.c b.c aa
  46 /etc/fstab
 124 a.c
  98 b.c
wc: aa: No such file or directory
 268 total

当然这只是一个简单的例子,我在此示例的基础上进行了扩展 线程的使用,例如通过线程计算总数 他们自己。在这种情况下,total必须是共享资源,您需要一个 mutex保护它,首先尝试实现它。