竞争条件问题,使用线程同步文本文件时

Race conditions problem, when synchronizing text files using threads

好吧,我开始研究 C 中的 POSIX 线程,我正在尝试创建每个线程,读取一行文件,直到好吧,我设法让它工作通过条件变量使用同步,但是在读取文件的第一行时,两个线程可以同时读取导致竞争条件问题,为什么会这样?我使用 valgrind 的 helgrind 工具对其进行了测试。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>


pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

/*

  $cat file.txt
  test1
  test2
  test3
  test4
  test5
  test6
*/

FILE *fp;

void *func(void) {

      char c;

      fp = fopen("file.txt", "r");
      if(!fp) {
          perror("DEBUG: ");
          exit(EXIT_FAILURE);
      }

      pthread_mutex_lock(&mutex);
      while((c = fgetc(fp)) != EOF) {
         printf("TH = %zu CHARACTER = %c\n", pthread_self(), c);
         if(c == '\n') {
              pthread_cond_wait(&cond, &mutex);
            } else {
              pthread_cond_signal(&cond);
          }
      }
      pthread_mutex_unlock(&mutex);

      pthread_exit(NULL);
 }

 int main(void) {

    pthread_t thread1, thread2;

    pthread_create(&thread1, NULL, (void *)func, NULL);
    sleep(1);
    pthread_create(&thread2, NULL, (void *)func, NULL);

    pthread_join(thread2, NULL);

    fclose(fp);

    return 0;
}  

当您的线程进入 func 时,它们都会调用 fp = fopen("file.txt", "r")。这意味着当 fp 指向的 FILE* 从第一个 运行 的线程下被拉出时,理论上您可以在读取文件的任何步骤中。

如果每个线程都需要读取整个文件,那么您应该将 FILE *fp 的声明拉到您打开文件的位置,这样它就是一个局部变量。如果您采用这种方法,请确保关闭两个 FILE*s.

如果他们共享当前设置的指针,那么您需要安全地打开该文件。根据您的需要,您可以通过两种方式完成此操作。

1) 您应该更改声明以将值也设置为 NULL。然后在 func 内将您的 pthread_mutex_lock(&mutex) 向上移动到对 fopen() 的调用之上。然后仅在 fp == NULL.

时调用 fopen()

2) 在启动 pthread 之前,您还可以将对 fopen() 的调用移至 main()。这使得只有一个线程必须担心打开文件并且它与您的 fclose(fp) 调用保持平衡,因此您知道 fp 的范围是一个有效的指针。

另请注意,加入所有线程通常是一种很好的做法。如果您使用的是 Valgrind,它会抱怨 thread1 泄漏,因为它没有连接。对于像这样的简单案例以及线程的行为方式,在这种情况下应该无关紧要,但如果您正在学习 pthreads,从一开始就养成一个好习惯。

两个线程都打开文件并将其分配给 fp

第一个线程打开文件并读取第一个条目后,下一个线程再次打开文件并读取第一个条目。

通过在 main 函数中打开文件并将 FILE * 作为参数传递给线程函数来解决此问题。这也通过给它一个 pthread_create 期望的签名来修复线程函数,即 void *(*)(void *).

void *func(void *param) {

      char c;

      FILE *fp = param;
      pthread_mutex_lock(&mutex);
      while((c = fgetc(fp)) != EOF) {
         printf("TH = %zu CHARACTER = %c\n", pthread_self(), c);
         if(c == '\n') {
              pthread_cond_wait(&cond, &mutex);
            } else {
              pthread_cond_signal(&cond);
          }
      }
      pthread_mutex_unlock(&mutex);

      pthread_exit(NULL);
 }

 int main(void) {

    pthread_t thread1, thread2;

      FILE *fp = fopen("file.txt", "r");
      if(!fp) {
          perror("DEBUG: ");
          exit(EXIT_FAILURE);
      }

    pthread_create(&thread1, NULL, func, fp);
    sleep(1);
    pthread_create(&thread2, NULL, func, fp);

    pthread_join(thread2, NULL);

    fclose(fp);

    return 0;
}