在 C 中同时使用条件变量和互斥量来同步线程
Using Both Conditional Variables & Mutex to Synchronize Threads in 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 mutex lock/unlock 以及信号和等待来完成这项工作。但是,我一直处于僵局状态。
有两个线程。目前,它们相互镜像,这意味着它们做同样的事情,只是使用不同的文件和相反的条件。
线程示例:
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 < //number)
{
pthread_mutex_lock(&lock);
//read line
//if we've reached a zero
{
w = 2;
while(w == 2)
{
pthread_cond_wait(&condition1, &lock);
}
pthread_mutex_unlock(&lock);
}
else
{
if(w == 1)
{
if(strlen(placeHolderChars)>0)
{
placeHolderChars[1] = '[=12=]';
}
//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 <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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] != '[=10=]'; 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] = '[=10=]';
}
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] = '[=10=]';
}
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;
}
您应该能够改进代码,以便将包含相关的每线程数据(文件名、当前线程条件、其他线程条件,可能是 'thread 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
]
h i h i h i h i h i
我对这段代码不是很满意。正如我暗示的那样,我创建了一个由两个线程使用的单个函数的修改版本,并修改了读取行的打印以避免打印换行符(使输出更紧凑)。有时——并非所有时候——它会在最后陷入僵局。两个示例跟踪,一个工作,一个死锁(程序名称 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]
h i h i h i h i h i
$ 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
$
我没有找到异常。它不像'thread one went first'那么简单;有一些例子,第一个线程先运行并且完成得很好。
我觉得提问题的人说了两遍,有点烦人。 FWIW,这是我对重复项的回答:
Pthread Synchronization: Back & Forth Reading of Two Text Files
根据评论者的要求进行了编辑。
这个程序创建了两个线程。每个线程从两个特定输入文件中的一个读取,每个文件的每行代码包含一个字母或一个“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 mutex lock/unlock 以及信号和等待来完成这项工作。但是,我一直处于僵局状态。
有两个线程。目前,它们相互镜像,这意味着它们做同样的事情,只是使用不同的文件和相反的条件。
线程示例:
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 < //number)
{
pthread_mutex_lock(&lock);
//read line
//if we've reached a zero
{
w = 2;
while(w == 2)
{
pthread_cond_wait(&condition1, &lock);
}
pthread_mutex_unlock(&lock);
}
else
{
if(w == 1)
{
if(strlen(placeHolderChars)>0)
{
placeHolderChars[1] = '[=12=]';
}
//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 <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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] != '[=10=]'; 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] = '[=10=]';
}
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] = '[=10=]';
}
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;
}
您应该能够改进代码,以便将包含相关的每线程数据(文件名、当前线程条件、其他线程条件,可能是 'thread 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
]
h i h i h i h i h i
我对这段代码不是很满意。正如我暗示的那样,我创建了一个由两个线程使用的单个函数的修改版本,并修改了读取行的打印以避免打印换行符(使输出更紧凑)。有时——并非所有时候——它会在最后陷入僵局。两个示例跟踪,一个工作,一个死锁(程序名称 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]
h i h i h i h i h i
$ 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
$
我没有找到异常。它不像'thread one went first'那么简单;有一些例子,第一个线程先运行并且完成得很好。
我觉得提问题的人说了两遍,有点烦人。 FWIW,这是我对重复项的回答: Pthread Synchronization: Back & Forth Reading of Two Text Files