竞争条件问题,使用线程同步文本文件时
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;
}
好吧,我开始研究 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;
}