使用条件变量和互斥量来同步C中的线程

根据评论者的要求编辑。

该程序创建两个线程。 每个线程从两个特定输入文件中的一个读取,每个输入文件包含一行字母或每行代码一个’0’。 线程应该将字母读入全局字符数组,然后打印出来。 问题是,在达到’0’时,活动线程必须将控制转移到另一个线程,该线程在该线路上不应该为’0’。 (我们确信,如果文件1在一行上有一个’0’,那么相应行上的文件2就有一个字母。多个零可以互相跟随,多个字母也可以。)

文件一

h 0 h 0 h 0 h 0 h 0 

文件二

 0 i 0 i 0 i 0 i 0 i 

我正在尝试使用pthread互斥锁定/解锁以及信号并等待使其工作。 但是,我一直处于僵局状态。

两个线程。 目前,它们彼此镜像意味着它们执行相同的操作,只是使用不同的文件和相反的条件。

线程示例:

 char final[1001]; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t condition1 = PTHREAD_COND_INITIALIZER; pthread_cond_t condition2 = PTHREAD_COND_INITIALIZER; int w = 1; void *get() { //start reading while (count 0) { placeHolderChars[1] = '\0'; } //copy char to array w= 2; pthread_cond_signal(&condition2); pthread_mutex_unlock(&lock); } } if(feof(file)) { fclose(file); break; } count++; } return 0; } 

更新:使用更大的文件时,信号等待前策略并没有真正起作用。 还在努力!

这段代码似乎有效。 主要的重大变化是在进入while (who == N)循环之前在另一个线程的条件上添加pthread_cond_signal()

其他更改包括基本调试打印,以便更容易查看哪个线程正在执行什么操作。 请注意,调试消息以换行符结尾。

 #include  #include  #include  #include  #include  extern void *getMessage1(void *arg); extern void *getMessage2(void *arg); static char message[4096]; int main(void) { pthread_t id1; pthread_t id2; pthread_create((&id1), NULL, getMessage1, NULL); pthread_create((&id2), NULL, getMessage2, NULL); pthread_join(id1, NULL); pthread_join(id2, NULL); for (int j = 0; j < 1001 && message[j] != '\0'; j++) printf("%c ", message[j]); putchar('\n'); return 0; } static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t condition1 = PTHREAD_COND_INITIALIZER; static pthread_cond_t condition2 = PTHREAD_COND_INITIALIZER; static int who = 1; void *getMessage1(void *arg) { assert(arg == NULL); const char filename[] = "Student1"; FILE *studentOne = fopen(filename, "r"); if (studentOne == NULL) { fprintf(stderr, "Failed to open file %s for reading\n", filename); exit(EXIT_FAILURE); } size_t howManyChars; char *placeHolderChars; int count = 1; while (count < 501) { placeHolderChars = NULL; if (getline(&placeHolderChars, &howManyChars, studentOne) == -1) break; printf("M1(%d): [%s]\n", count, placeHolderChars); pthread_mutex_lock(&lock); if (strcmp(placeHolderChars, "0\n") == 0) { printf("M1: Two's turn - 1\n"); pthread_cond_signal(&condition2); who = 2; while (who == 2) { pthread_cond_wait(&condition1, &lock); } free(placeHolderChars); } else { if (who == 1) { if (strlen(placeHolderChars) > 0) { placeHolderChars[1] = '\0'; } strcat(message, placeHolderChars); free(placeHolderChars); who = 2; pthread_cond_signal(&condition2); } else printf("M1: Two's turn - 2\n"); } pthread_mutex_unlock(&lock); count++; } fclose(studentOne); return 0; } void *getMessage2(void *arg) { assert(arg == NULL); const char filename[] = "Student2"; FILE *studentTwo = fopen(filename, "r"); if (studentTwo == NULL) { fprintf(stderr, "Failed to open file %s for reading\n", filename); exit(EXIT_FAILURE); } size_t howManyChars; char *placeHolderChars; int count = 0; while (count < 501) { placeHolderChars = NULL; if (getline(&placeHolderChars, &howManyChars, studentTwo) == -1) break; printf("M2(%d): [%s]\n", count, placeHolderChars); pthread_mutex_lock(&lock); if (strcmp(placeHolderChars, "0\n") == 0) { printf("M2: One's turn - 1\n"); pthread_cond_signal(&condition1); who = 1; while (who == 1) { pthread_cond_wait(&condition2, &lock); } free(placeHolderChars); } else { if (who == 2) { if (strlen(placeHolderChars) > 0) { placeHolderChars[1] = '\0'; } strcat(message, placeHolderChars); free(placeHolderChars); who = 1; pthread_cond_signal(&condition1); } else printf("M2: One's turn - 2\n"); } pthread_mutex_unlock(&lock); count++; } fclose(studentTwo); return 0; } 

您应该能够优化代码,以便将包含相关的每线程数据(文件名,当前线程条件,其他线程条件,可能是’线程ID’)的结构传递给单个函数,因此您只需要getMessage()

输出:

 M1(1): [h ] M2(0): [0 ] M1(2): [0 ] M2: One's turn - 1 M1: Two's turn - 1 M2(1): [i ] M2(2): [0 ] M2: One's turn - 1 M1(3): [h ] M1(4): [0 ] M1: Two's turn - 1 M2(3): [i ] M2(4): [0 ] M2: One's turn - 1 M1(5): [h ] M1(6): [0 ] M1: Two's turn - 1 M2(5): [i ] M2(6): [0 ] M2: One's turn - 1 M1(7): [h ] M1(8): [0 ] M1: Two's turn - 1 M2(7): [i ] M2(8): [0 ] M2: One's turn - 1 M1(9): [h ] M1(10): [0 ] M1: Two's turn - 1 M2(9): [i ] hihihihihi 

我对这段代码并不满意。 我创建了一个修改版本,其中包含两个线程使用的单个函数,正如我所暗示的那样,并修改了读取的行的打印以避免打印换行符(使输出更紧凑)。 有时 – 不是所有的时间 – 它最终会陷入僵局。 两个示例跟踪,一个工作,一个死锁(程序名称第pth47 ):

 $ pth47 M2(1): [0] M2: 1's turn - 1 M1(1): [h] M1(2): [0] M1: 2's turn - 1 M2(2): [i] M2(3): [0] M2: 1's turn - 1 M1(3): [h] M1(4): [0] M1: 2's turn - 1 M2(4): [i] M2(5): [0] M2: 1's turn - 1 M1(5): [h] M1(6): [0] M1: 2's turn - 1 M2(6): [i] M2(7): [0] M2: 1's turn - 1 M1(7): [h] M1(8): [0] M1: 2's turn - 1 M2(8): [i] M2(9): [0] M2: 1's turn - 1 M1(9): [h] M1(10): [0] M1: 2's turn - 1 M2(10): [i] hihihihihi $ pth47 M1(1): [h] M2(1): [0] M1(2): [0] M2: 1's turn - 1 M1: 2's turn - 1 M2(2): [i] M2(3): [0] M2: 1's turn - 1 M1(3): [h] M1(4): [0] M1: 2's turn - 1 M2(4): [i] M2(5): [0] M2: 1's turn - 1 M1(5): [h] M1(6): [0] M1: 2's turn - 1 M2(6): [i] M1(7): [h] M1(8): [0] M2(7): [0] M1: 2's turn - 1 M2: 1's turn - 1 M1(9): [h] M1(10): [0] M2(8): [i] M1: 2's turn - 1 M2(9): [0] M2: 1's turn - 1 ^C $ 

我没有追踪失常。 它并不像’第一个线程第一个’那么简单; 有一些例子,第一个线程首先完成,并且完成得很好。

我认为提出这个问题的人做了两次,有点烦人。 FWIW,这是我对副本的回答: Pthread同步:返回和读取两个文本文件