3 信号量的线程和消费者-生产者问题
3 Threads and Consumer-Producer problem with Semaphores
这里是线程并发新手。所以,我的 encrypt_thread 有时会挂起,我很确定这是因为我使用信号量的方式。
我的程序做什么:
read_from_file 是一个线程,它一次将文件中的字符读入 input_buffer 一个字符。每次这样做,它都会将 empty_in_input_buffer 减 1,并将 full_in_input_buffer 加 1。如果 empty_in_input 为 0,它会等待 post (&empty_in_input_buffer) 来自加密线程。
加密线程等待来自读取线程的 post(&full_in_input_buffer) 开始字符加密。然后调用 post(&empty_in_input_buffer),然后调用 wait(&empty_in_output_buffer)。一旦将加密字符放入 output_buffer,它就会调用 post(&empty_in_output_buffer)。加密是 "middleman," 等待将新字符放入 input_buffer 并等待 output_buffer.
中的新插槽可用
写线程调用wait(&full_in_output_buffer)然后恢复执行,将output_buffer中的字符写入文件.一旦完成,它会调用 post(&empty_in_output_buffer).
我想达到最大并发度,这样,如果read_from_file在input_buffer的第5槽放置一个新字符,那么encrypt仍然可以访问第3槽和第4槽进行加密和写入一个 output_buffer。我没有使用互斥锁来锁定 input_buffer,因为缓冲区中的项目未被修改,仅被读取、加密并放入 output_buffer,但我的假设可能是错误的需要一个。
该程序还在一开始就提示用户输入 buffer_size。问题是,我的程序有一半时间挂起,另一半时间运行良好,这表明我有一个我没有考虑的竞争条件。但是,我一直无法弄清楚它发生在哪里。
编辑:fill_letter_arr 只是用大写和小写字母创建一个字符数组。 is_in_letters 只是检查一个字符是否是字母表中的一个字母。我的加密非常简单,它只加密字母。
主线程:
int main(int argc, char *argv[]){
//sem_init(&encrypt_signals_read, 0, 1);
int buf_size;
file_in = fopen(argv[1], "r");
file_out = fopen(argv[2], "w");
take_input(&buf_size);
struct thread_args args = {
malloc(sizeof(char)*buf_size),
malloc(sizeof(char)*buf_size),
buf_size,
0,
};
sem_init(&empty_in_input,0,buf_size);
sem_init(&empty_in_output,0,buf_size);
sem_init(&full_in_input_buffer,0,0);
sem_init(&full_in_output_buffer,0,0);
//creating threads
pthread_t read_thread,encrypt_thread,write_thread;
if(pthread_create(&read_thread,NULL,read_from_file,&args) != 0){
printf("Error creating read thread!");
}
if(pthread_create(&encrypt_thread,NULL,encrypt,&args) != 0){
printf("Error creating encrypt thread!");
}
if(pthread_create(&write_thread,NULL,write_to_file,&args) != 0){
printf("Error creating write thread!");
}
pthread_join(read_thread,NULL);
pthread_join(encrypt_thread,NULL);
pthread_join(write_thread,NULL);
fclose(file_in);
fclose(file_out);
}
阅读线程:
void* read_from_file(void* args){
struct thread_args* shared = (struct thread_args*) args;
char c = '0';
int i = 0;
int val,val1,val2,val3;
if (file_in != NULL){
do{
c = fgetc(file_in);
if(i >= shared->buffer_size)
i = 0;
sem_wait(&empty_in_input);
shared->input_buffer[i] = c;
sem_post(&full_in_input_buffer);
i++;
}while(c != EOF);
}
if (ferror(file_in) != 0 ) {
fputs("Error reading file", stderr);
exit(1);
}
}
加密线程:
void* encrypt(void* args){
struct thread_args* shared = (struct thread_args*) args;
int s = 1;
int i = 0;
char c = '0';
int val,val1,val2,val3,val4;
fill_letters_arr(0);
do{
if(i >= shared->buffer_size)
i = 0;
sem_wait(&full_in_input_buffer);
c = shared->input_buffer[i];
sem_post(&empty_in_input);
if(is_in_letters(&c) == true){
encrypt_letter(&s,&c);
}
sem_wait(&empty_in_output);
shared->output_buffer[i] = c;
sem_post(&full_in_output_buffer);
i++;
}while(c != EOF);
}
写线程:
void* write_to_file(void* args){
struct thread_args* shared = (struct thread_args*) args;
char c = '0';
int i = 0;
int val,val1,val2,val3;
if (file_out != NULL){
while(c != EOF){
if(i >= shared->buffer_size)
i = 0;
sem_wait(&full_in_output_buffer);
c = shared->output_buffer[i];
fputc(c,file_out);
sem_post(&empty_in_output);
i++;
}
}
if (ferror(file_in) != 0 ) {
fputs("Error reading file", stderr);
}
}
您没有 post 完整的、可重现的程序,所以猜测:
1)您对 EOF 的使用可疑。通常,您将 getc 的 return 视为一个 int;它可以涵盖每个有效字符值以及 EOF。这也可以保护您免受认为无符号字符是合理默认值的编译器的影响。无论如何,您可能应该选择一个不同的标志值来通过您的缓冲区以指示 EOF。
在 :
if(is_in_letters(&c) == true){
encrypt_letter(&s,&c);
}
2) 因为你给它一个负值(EOF 将截断为 (char)-1),所以如果你将它用作索引,你可能会得到一些令人惊讶的值。
3) 如果你的encrypt_letter恰好生成一个0xff作为加密版本,它会被这个和最后阶段解释为EOF;但第一阶段还是会尝试愉快地读字
总结:EOF不是一个字符值,它是一个int。
这里是线程并发新手。所以,我的 encrypt_thread 有时会挂起,我很确定这是因为我使用信号量的方式。
我的程序做什么:
read_from_file 是一个线程,它一次将文件中的字符读入 input_buffer 一个字符。每次这样做,它都会将 empty_in_input_buffer 减 1,并将 full_in_input_buffer 加 1。如果 empty_in_input 为 0,它会等待 post (&empty_in_input_buffer) 来自加密线程。
加密线程等待来自读取线程的 post(&full_in_input_buffer) 开始字符加密。然后调用 post(&empty_in_input_buffer),然后调用 wait(&empty_in_output_buffer)。一旦将加密字符放入 output_buffer,它就会调用 post(&empty_in_output_buffer)。加密是 "middleman," 等待将新字符放入 input_buffer 并等待 output_buffer.
中的新插槽可用
写线程调用wait(&full_in_output_buffer)然后恢复执行,将output_buffer中的字符写入文件.一旦完成,它会调用 post(&empty_in_output_buffer).
我想达到最大并发度,这样,如果read_from_file在input_buffer的第5槽放置一个新字符,那么encrypt仍然可以访问第3槽和第4槽进行加密和写入一个 output_buffer。我没有使用互斥锁来锁定 input_buffer,因为缓冲区中的项目未被修改,仅被读取、加密并放入 output_buffer,但我的假设可能是错误的需要一个。
该程序还在一开始就提示用户输入 buffer_size。问题是,我的程序有一半时间挂起,另一半时间运行良好,这表明我有一个我没有考虑的竞争条件。但是,我一直无法弄清楚它发生在哪里。
编辑:fill_letter_arr 只是用大写和小写字母创建一个字符数组。 is_in_letters 只是检查一个字符是否是字母表中的一个字母。我的加密非常简单,它只加密字母。
主线程:
int main(int argc, char *argv[]){
//sem_init(&encrypt_signals_read, 0, 1);
int buf_size;
file_in = fopen(argv[1], "r");
file_out = fopen(argv[2], "w");
take_input(&buf_size);
struct thread_args args = {
malloc(sizeof(char)*buf_size),
malloc(sizeof(char)*buf_size),
buf_size,
0,
};
sem_init(&empty_in_input,0,buf_size);
sem_init(&empty_in_output,0,buf_size);
sem_init(&full_in_input_buffer,0,0);
sem_init(&full_in_output_buffer,0,0);
//creating threads
pthread_t read_thread,encrypt_thread,write_thread;
if(pthread_create(&read_thread,NULL,read_from_file,&args) != 0){
printf("Error creating read thread!");
}
if(pthread_create(&encrypt_thread,NULL,encrypt,&args) != 0){
printf("Error creating encrypt thread!");
}
if(pthread_create(&write_thread,NULL,write_to_file,&args) != 0){
printf("Error creating write thread!");
}
pthread_join(read_thread,NULL);
pthread_join(encrypt_thread,NULL);
pthread_join(write_thread,NULL);
fclose(file_in);
fclose(file_out);
}
阅读线程:
void* read_from_file(void* args){
struct thread_args* shared = (struct thread_args*) args;
char c = '0';
int i = 0;
int val,val1,val2,val3;
if (file_in != NULL){
do{
c = fgetc(file_in);
if(i >= shared->buffer_size)
i = 0;
sem_wait(&empty_in_input);
shared->input_buffer[i] = c;
sem_post(&full_in_input_buffer);
i++;
}while(c != EOF);
}
if (ferror(file_in) != 0 ) {
fputs("Error reading file", stderr);
exit(1);
}
}
加密线程:
void* encrypt(void* args){
struct thread_args* shared = (struct thread_args*) args;
int s = 1;
int i = 0;
char c = '0';
int val,val1,val2,val3,val4;
fill_letters_arr(0);
do{
if(i >= shared->buffer_size)
i = 0;
sem_wait(&full_in_input_buffer);
c = shared->input_buffer[i];
sem_post(&empty_in_input);
if(is_in_letters(&c) == true){
encrypt_letter(&s,&c);
}
sem_wait(&empty_in_output);
shared->output_buffer[i] = c;
sem_post(&full_in_output_buffer);
i++;
}while(c != EOF);
}
写线程:
void* write_to_file(void* args){
struct thread_args* shared = (struct thread_args*) args;
char c = '0';
int i = 0;
int val,val1,val2,val3;
if (file_out != NULL){
while(c != EOF){
if(i >= shared->buffer_size)
i = 0;
sem_wait(&full_in_output_buffer);
c = shared->output_buffer[i];
fputc(c,file_out);
sem_post(&empty_in_output);
i++;
}
}
if (ferror(file_in) != 0 ) {
fputs("Error reading file", stderr);
}
}
您没有 post 完整的、可重现的程序,所以猜测: 1)您对 EOF 的使用可疑。通常,您将 getc 的 return 视为一个 int;它可以涵盖每个有效字符值以及 EOF。这也可以保护您免受认为无符号字符是合理默认值的编译器的影响。无论如何,您可能应该选择一个不同的标志值来通过您的缓冲区以指示 EOF。
在 :
if(is_in_letters(&c) == true){
encrypt_letter(&s,&c);
}
2) 因为你给它一个负值(EOF 将截断为 (char)-1),所以如果你将它用作索引,你可能会得到一些令人惊讶的值。
3) 如果你的encrypt_letter恰好生成一个0xff作为加密版本,它会被这个和最后阶段解释为EOF;但第一阶段还是会尝试愉快地读字
总结:EOF不是一个字符值,它是一个int。