【问题标题】:Race Conditions with pthreads in CC 中 pthread 的竞争条件
【发布时间】:2012-05-11 10:39:12
【问题描述】:

我是 C 语言中的 pthreads 的新手,我正在编写一个简单的程序,可以在多个文件中并行查找单词。但是,每当我输入多个文件时,输出都会发生变化,这表明存在我没有在我的代码中修复的竞争条件。你能帮我修一下吗?

下面的sn-p在main中,制作pthreads。

    int i = 0;
char *word = "Pluto"; //Word to be found

Message messages[argc-1];
pthread_t threads[argc-1];
for(i; i < argc - 1; i++){
    messages[i].file = argv[i + 1];
    messages[i].word = word;
    messages[i].fp   = fopen(argv[i + 1], "r");
    int  iret = pthread_create( &threads[i], NULL, threadFindWord, (void*) &(messages[i]));
}for(i = 0; i < argc - 1; i++){
    pthread_join(threads[i],NULL);
}

每个线程调用的函数:

Message *msg;
msg = (Message *) ptr;

int numFound = ffindWord(msg->fp, msg->word);

printf("File %s has %i occurences of the word %s\n", msg->file, numFound, msg->word);

fclose(msg->fp);
pthread_exit(NULL);

以下是在文件中查找单词的代码)

int findWord(char * file, char * word){
 char * current = strtok(file, " ,.\n");
 int sum = 0;
 while (current != NULL){
    //printf("%s\n", current);
    if(strcmp(current, word) == 0)
        sum+=1;
    current = strtok(NULL, " ,.\n");
}
return sum;
}



int ffindWord(FILE *fp, char *word){

 fseek(fp, 0, SEEK_END);
 long pos = ftell(fp);
 fseek(fp, 0, SEEK_SET);
 char *bytes = malloc(pos);
 fread(bytes, pos, 1, fp);
 bytes[pos-1] = '\0';

 int sum = findWord(bytes, word);

 free(bytes);
 return sum;
 }

为了澄清,问题是我在连续运行程序时得到不同的结果。一个电话 $程序名 文件1 文件2 打印与之后调用的相同调用不同的结果。但是请注意,当只传递一个文件时,该程序可以工作。

感谢任何帮助。

【问题讨论】:

  • 输出如何变化?来自同一文件或混合输出的不同结果?
  • @hmjd 是的,输出不同。例如:$ ./a.out blu bla 输出 File blu has 0 occurences of the word Pluto File bla has 5 occurences of the word Pluto 和 $ ./a.out blu bla 之后的相同命令输出 File bla has 1 occurences of the word Pluto File blu has 4 occurences of the word Pluto
  • 无需投射到void*或从void*
  • @JensGustedt 演员阵容不应该真正影响输出
  • 重复运行$ ./a.out blu,是否得到一致的结果?你能发布ffindWord()函数定义吗?还有你用来构建的编译器命令。

标签: c pthreads race-condition file-pointer


【解决方案1】:

strtok 保留一个全局内部指针...使用strtok_r。

【讨论】:

    【解决方案2】:

    这会导致未定义的行为,因为它超出了 messages 和 threads 数组的末尾:

    Message messages[argc-1];
    pthread_t threads[argc-1];
    for(i; i < argc; i++){
    

    并且可能是问题的原因。当仅执行 1 个线程时,它可能会偶然工作。

    尝试更改为(或类似的):

    int i;
    Message messages[argc-1];
    pthread_t threads[argc-1];
    for(i = 1; i < argc; i++)
    {
        messages[i - 1].file = argv[i];
        messages[i - 1].word = word;
        messages[i - 1].fp   = fopen(argv[i], "r");
        int iret = pthread_create(&threads[i - 1],
                                   NULL,
                                   threadFindWord,
                                   (void*)&(messages[i - 1]));
    }
    
    for(i = 0; i < argc - 1; i++)
    {
        pthread_join(threads[i],NULL);
    } 
    

    【讨论】:

    • Hmm.. 循环应该从 0 运行到 (argc-1) 以便为数组生成正确的基于 0 的索引。
    • @MartinJames,在我的示例解决方案中,i - 1 用于索引第一个 for 中的 messages 和 threads。 OP 想跳过 argv[0] 因为它是程序名称。
    • 如果 index 保持为 0,则更容易理解和编码。在 for() 内部执行一个简单的 if(i==0) continue。
    • 我按照你的建议改了,但是问题依旧
    • @pretobomba,所以也许您应该尝试更好地描述问题所在。你期待什么——你会得到什么。
    【解决方案3】:

    为避免每个线程的输出以随机方式混合在一起,您需要缓冲每个线程的输出,然后一次显示一个。

    对于您的情况,最简单的方法是将char *thread_output 字段(和thread_output_size 字段)添加到Message 结构中,然后在您的主线程中执行以下操作:

    for(i = 0; i < argc - 1; i++)
    {
        pthread_join(threads[i],NULL);
        printf("%s", messages[i - 1].thread_output);
    }
    

    您可能还想实现一个函数,确保thread_output 缓冲区足够大,然后使用vsnprintf() 将新文本添加到缓冲区,这样您就可以像以前一样使用它printf().

    例如:

    void add_thread_output(int thread_number, const char *template, ...) {
        int old_length;
        int length;
        char *temp;
    
        va_start(ap, template);
        length = vsnprintf(NULL, 0, template, ap);
        va_end(ap);
    
        old_length = messages[thread_number - 1].thread_output_size;
        temp = realloc(messages[thread_number - 1].thread_output, old_length + length + 1);
        if(temp == NULL) {
            /* Set a flag or something */
        } else {
            va_start(ap, template);
            vsnprintf(&temp[old_length], length + 1, template, ap);
            va_end(ap);
            messages[thread_number - 1].thread_output_size += length;
            messages[thread_number - 1].thread_output = temp;
        }
    }
    

    注意:上面的任何示例代码仅用于示例目的,未经测试或保证编译或工作,也不一定是最有效的方法。例如。分配比您需要更多的空间(避免每次向线程的输出缓冲区添加内容时都执行realloc())可能是个好主意。

    【讨论】:

      猜你喜欢
      • 2013-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-27
      • 2017-03-20
      相关资源
      最近更新 更多