使用pthreads逐行读取文件...意外退出

时间:2013-02-23 16:02:32

标签: c pthreads fopen fread pthread-join

我有以下代码:

    /*//not important
    FILE * INFILE;
    list_file = optarg;
    if( ( INFILE = fopen( list_file, "a+" ) ) == NULL ) {
        fprintf( stderr, "Can't open input file\n");
        exit(0);
    }
    */

    pthread_mutex_t input_queue;
    pthread_mutex_init(&input_queue, NULL);

    for( i = 0 ; i < number_thread; i++)
    {
        if( pthread_create( &thread_id[i], NULL, &work, NULL) != 0 )
        {
            i--;
            fprintf(stderr, RED "\nError in creating thread\n" NONE);
        }
    }
    for( i = 0 ; i < number_thread; i++)
        if( pthread_join( thread_id[i], NULL) != 0 )
        {
            fprintf(stderr, RED "\nError in joining thread\n" NONE);
        }




    void * work(void * data)
    {
        unsigned long line;
        char buf[512];
        while ( !feof(INFILE) )
        {
            pthread_mutex_lock(&input_queue);
            fgets((char *)&buf, sizeof(buf), INFILE);
            if (buf[strlen (buf) - 1] == '\n')
                buf[strlen (buf) - 1] = '\0';
            line = (unsigned long)buf;
            pthread_mutex_unlock(&input_queue);
            do_work( line );
        }
        fclose(INFILE);
        return NULL;
    }

它从文件中读取行但是一段时间后它意外退出,没有错误消息。 我猜我搞砸了。

如何使用pthreads逐行读取文件,但尽可能保持代码不变(我的意思是不要弄乱整个程序)? < / p>

2 个答案:

答案 0 :(得分:2)

您在遇到EOF的第一个帖子中关闭INFILE。之后其他线程将在已关闭的文件上调用feof() - 可能还有fclose() - 这将破坏堆并几乎肯定会导致崩溃。此外,您的换行代码可能会在EOF中欠载缓冲区,请参阅下面的注释。

要解决此问题,请使用相同的互斥锁保护feof()fclose(),并将INFILE设置为NULL。获取互斥锁时,检查INFILE是否为NULL并立即返回:

for (;;) {
  pthread_mutex_lock(&input_queue);
  if (!INFILE) {
    pthread_mutex_unlock(&input_queue);
    break;
  }
  if (feof(INFILE)) {
    INFILE = NULL;
    pthread_mutex_unlock(&input_queue);
    break;
  }

  fgets(buf, sizeof(buf), INFILE);
  pthread_mutex_unlock(&input_queue);

  // ...strip newline, do_work...
}

几条评论:

  • 您的代码会在不检查buf[strlen(buf) - 1]是否为零的情况下写入strlen(buf)。 EOF会buf为空,所以这不是理论上的问题,每次执行都会发生一次。

  • line的类型为unsigned long,但您要为其指定一个指针值。这将在long不包含指针的平台上失败,例如Win64。将linedo_work的参数声明为char *(如果必须接受其他指针类型,则为void *)。

  • 避免将您的互斥锁称为“队列”;在多线程编程队列中引用producer-consumer aware FIFO

  • 您不需要使用互斥锁保护fgets之类的各个stdio函数。根据POSIX的要求,它们是MT安全的。 (但是,在我修改过的代码中,fgets()确实需要受互斥保护,因为INFILE可能会在未保留互斥锁时失效。)

  • (char *) &buf没有意义。由于bufchar数组,因此它已经衰减为指向其第一个成员的指针,因此您只需将buf发送到fgets即可。如果您坚持使用address-of运算符,则正确的表达式为&buf[0]

  • 正如Carl Norum暗示的那样,feof()可能不是您想要的,因为它只检测fgets()已经遇到的EOF条件。检查EOF的正确方法是测试fgets()在剥离换行符之前是否返回空字符串 -

答案 1 :(得分:1)

如果INFILE是一个全局变量,那么你已经关闭了线程函数中的参考,如果你创建了多个线程,那么在其他线程中flcose(INFILE),即fclose(NULL)预计会崩溃。无法猜测你试图用多个线程做什么,但最好在你确定INFILE不再被任何其他线程访问时最终关闭它。我认为你应该在所有线程之后关闭主要的INFILE引用加入main并完成他们的处理。

#include<stdio.h>
#include<pthread.h>
#include<string.h>
#include<stdlib.h>
#define number_thread 10

FILE * INFILE;
char *list_file = "test_thread";
pthread_mutex_t input_queue;

void do_work(unsigned long buf)
{
    printf("working on %u\n",buf);
}

void * work(void * data)
{
    unsigned long line;
    char buf[512];
    printf("IAM NEW THREAD\n" );

    while ( !feof(INFILE) )
      {
        pthread_mutex_lock(&input_queue);
        fgets((char *)&buf, sizeof(buf), INFILE);
        if (buf[strlen (buf) - 1] == '\n')
            buf[strlen (buf) - 1] = '\0';
        line = (unsigned long)buf;
        pthread_mutex_unlock(&input_queue);
        do_work( line );
      }

    return NULL;
}

int main()
{
    printf("IAM MAIN THREAD\n")
    pthread_mutex_init(&input_queue, NULL);
    if( ( INFILE = fopen( list_file, "a+" ) ) == NULL ) {
        fprintf( stderr, "Can't open input file\n");
        exit(0);
    }
    pthread_t thread_id[10];

    int i=0;
    for( i = 0 ; i < number_thread; i++)
      { 
        if( pthread_create( &thread_id[i], NULL, &work, NULL) != 0 )
          {
            i--;
            fprintf(stderr,  "\nError in creating thread\n");
          }
      }

    for( i = 0 ; i < number_thread; i++)
        if( pthread_join( thread_id[i], NULL) != 0 )
          {
            fprintf(stderr,  "\nError in joining thread\n" );
          }

    fclose(INFILE);
}