【问题标题】:C multi-thread wordcount program segmentation fault issueC多线程wordcount程序分段错误问题
【发布时间】:2015-11-08 19:04:48
【问题描述】:

几个月来,我一直在为这个程序绞尽脑汁。这是我上学期上的一门课的作业,虽然我通过了,但我永远无法正确地完成这门课(Seg Fault)。我欢迎任何帮助或提示,但我非常感谢您的解释和答案。

这个程序应该接收一个包含文件名列表的文件名(我的例子是 240)。这些文件位于与列表和程序相同的目录中的文件夹中。该程序应该获取此列表并将其解析为 4 个线程,为每个线程平均拆分文件名(对于我的示例,每个线程 60 个)。然后每个线程获取 60 个文件名的列表,并一个一个地打开每个文件,对每个文件执行 WordCount 函数。一旦线程完成他们的任务,他们应该按顺序打印每个文件的结果,每个线程在它自己的块中(即线程1结果|线程2结果|线程3结果,等等......)。

我已经调试了很多,并且知道在创建线程之前,一切都按预期工作。我的问题似乎是在线程启动/执行期间。我尝试将互斥锁添加到混合中,但遗憾的是它没有帮助。当我的一些同学向我展示了他们更紧凑的代码时,我似乎遗漏了一些东西或过度思考了一些东西。请协助。谢谢!

这里是主要的:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define MaxLine 200
#define NUMTHREADS 4

char Line[MaxLine];
pthread_mutex_t Lock = PTHREAD_MUTEX_INITIALIZER;

typedef struct thread {
    int id;
    char file;
}ThreadData;

/* ThreadFunction will give each thread its processes to execute */
 void *threadFunc (void *td) {
    ThreadData *data = (ThreadData*)td;
    int thread_num=data->id;
    char filename=data->file;
    printf("thread debug tid: %d and file: %c",thread_num, filename);
    pthread_mutex_trylock(&Lock);
    FILE *fn = fopen(filename, "r");
    if (fn == NULL) {
        error("ERROR: Opening file");
        return 1;
    }
    while (fgets(Line, sizeof(Line), fn) != NULL) {
        CountWord(thread_num, Line);
    }
    fclose(fn);
    free(data);
    pthread_mutex_unlock(&Lock);
    pthread_exit(NULL);
}

int main(int argc, char *argv[]){
    char buf[20];
    int c, i, t, tnum, QUEUETOTAL;
    pthread_t thread[NUMTHREADS];
    ThreadData td[NUMTHREADS];

    if (argc != 2){
        fprintf(stderr,"ERROR: Usage must be Countfile filename\n", argv[0]);
        exit(0);
    }
    char const* const filename = argv[1];
    FILE* file = fopen(filename, "r");
    if ( file == 0 ){
        printf( "Could not open file!\n" );
        exit(0);
    }

    /* Count iterations of while loop to divide files among threads. */
    while (fgets(Line, sizeof(Line), file)){
        QUEUETOTAL++;
    }

    /* Divide work for threads. */
    int thread2taskstart=(QUEUETOTAL/NUMTHREADS); //60
    int thread3taskstart=(QUEUETOTAL/NUMTHREADS)*2; //120
    int thread4taskstart=(QUEUETOTAL/NUMTHREADS)*3; //180
    // QUEUETOTAL = 240

    rewind(file);
    FILE *tempfile1  = fopen("temp1.txt","w");
    for (i=0; i<thread2taskstart; i++) {
        // populate tempfile1 with entries 1-60
        if(fgets(Line,sizeof(Line),file)!=NULL) {
            fputs(Line,tempfile1);
            //printf("Debug temp1: %s",Line);
        }
    }
    fclose(tempfile1);
    FILE *tempfile2  = fopen("temp2.txt","w");
    for (i=thread2taskstart; i<thread3taskstart; i++) {
        // populate tempfile2 with entries 60-120
        if(fgets(Line,sizeof(Line),file)!=NULL) {
            fputs(Line,tempfile2);
            //printf("Debug temp2: %s",Line);
        }
    }
    fclose(tempfile2);
    FILE *tempfile3  = fopen("temp3.txt","w");
    for (i=thread3taskstart; i<thread4taskstart; i++) {
        // populate tempfile3 with entries 120-180
        if(fgets(Line,sizeof(Line),file)!=NULL) {
            fputs(Line,tempfile3);
            //printf("Debug temp3: %s",Line);
        }
    }
    fclose(tempfile3);
    FILE *tempfile4  = fopen("temp4.txt","w");
    for (i=thread4taskstart; i<=QUEUETOTAL; i++) {
        // populate tempfile3 with entries 180-240
        if(fgets(Line,sizeof(Line),file)!=NULL) {
            fputs(Line,tempfile4);
            //printf("Debug temp4: %s",Line);
        }
    }
    fclose(tempfile4);
    fclose(file);

    /* Prepare parameters & launch (4) threads.  Wait for threads
    to finish & print out results as specified in assignment. */
    printf("Counting files …\n");

    for(t=0;t<NUMTHREADS;t++){
        tnum=t+1;
        snprintf(buf, "temp%d.txt", tnum);
        printf("debug tnum and array: %d and %s\n",tnum, buf);
        td[t].id = tnum;
        td[t].file = buf;
        // Creates a new thread for each temp file.
        pthread_create(&thread[t], NULL, threadFunc, td);
    }
    // Joins threads.
    printf("debug: printing threads \n");
    for(t=0;t<NUMTHREADS;t++){
    pthread_join(thread[t], NULL);
    printf("-------------------------  Processes finished for Thread %d  ----------------------- \n",t+1);
    }
    return 0;
}

这里是计数函数:

#include <stdio.h>

int CountWord(int tinfo, char cfile){
    int i;
    int ccount = 0;
    int wcount = 0;
    int lcount = 0;
    FILE *fname;
    char fn[strlen(cfile) + 18];
    sprintf(fn, "./CountingFolder/%s", cfile);
    printf("Debug: %s\n", fn);
    fname = fopen(fn, "r");
    if (fname == NULL) {
        error("ERROR: Opening file");
    }
    while ((i = fgetc(fname)) != EOF){
        if (i == '\n') {
            lcount++;
        }
        if (i == '\t' || i == ' '){
            wcount++;
        }
        ccount++;
    }
    printf("Threadid %d processes %s which has %d characters, %d words and %d lines\n", tinfo, cfile, ccount, wcount, lcount);
    fclose(fname);
    return 0;
}

【问题讨论】:

  • 您正在使用pthread_mutex_trylock 而不检查结果。获取锁失败怎么办?
  • 函数中:CountWord():当文件打开失败时,不要尝试从文件中读取
  • 在函数中:CountWord():在'while'循环中:考虑输入文件中的一行包含制表符和空格的组合而没有其他字符的情况,包括/尤其是在开头的线。正确的文本在每个句子的结尾后有 2 个空格。建议:1)在stackoverflow中搜索从文件中提取单词的示例(可能没有多个线程)。好的示例将有两个状态机实现 in-word 和 not-in-word 。以非单词状态开始。
  • 如果任何输入文件的最后一个字符没有换行符,则行数和字数将不正确
  • 如果可执行文件和输入文件都在同一个目录下,那为什么要使用输入文件的路径呢?

标签: c multithreading segmentation-fault pthreads


【解决方案1】:

在您的代码中

snprintf(buf, "temp%d.txt", tnum);
printf("debug tnum and array: %d and %s\n",tnum, buf);
td[t].id = tnum;
td[t].file = buf;

最后一行分配了一个指向该结构的file 字段的指针

typedef struct thread {
    int id;
    char file;
}ThreadData;

不应该是char *file; 吗?我在 MSVC 中没有 thread.h,所以我无法编译它。您确定启用了所有警告吗??

【讨论】:

  • 如果是这种情况,它应该无法编译。这里肯定发生了一些奇怪的事情(可能缺少深拷贝),但这也可能是问题的一个问题。
【解决方案2】:

1) 可能是错字。但是

int CountWord(int tinfo, char cfile){ .. }

应该是

int CountWord(int tinfo, char *cfile){ .. }

2) 您将相同的buf 传递给来自main() 的所有线程。数据竞争和未定义的行为。

3) 没有一个snprintf() 调用采用size 参数。未定义的行为。

4) 由于所有线程都处理不同的数据,因此您根本不需要锁。

5) 你没有分配td 数组。所以不能在线程函数中调用free(data);。未定义的行为。

代码可能存在更多问题,但段错误可能是由于 (3) 或 (5)。

【讨论】:

  • 对。不需要使用锁,因为读取行的变量是全局的。但它确实应该是一个局部变量。
  • @BlueMoon:全局不需要锁定?那为什么呢?
  • @LightnessRacesinOrbit 我的意思是(在我之前的评论中)一个仅用于读取行并且应该是本地的变量,这不是在代码中引入锁定的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 2016-02-09
  • 2015-09-10
  • 2015-06-09
  • 1970-01-01
相关资源
最近更新 更多