c - 生产者-消费者问题中的无限循环
问题描述
我试图更好地理解编程中的fork()
并发c
性。我是新手,我无法理解逻辑。我尝试使用fork()
. 基本上,一个producer()
函数应该从 中获取一个字符stdin
,并将其写入文件。同时,第二个进程运行consumer
代码,它应该读取文件中的最后一个字符并将其回显到屏幕上。producer()
andconsumer()
函数本身工作正常,即它们正在做每个人应该做的事情,但问题在于并发性。这是我的代码:
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
FILE* fp;
//char c;
void producer(){
char c=' ';
while(c!='x'){
puts("enter a char");
c = getchar();
while((fp = fopen("shared.txt", "at"))==NULL); //while the file is in use by another program
fputc(c,fp);
if(c!='\n')puts("file written to successfully");
fclose(fp);
}
return;
}
char readChar(){
char c;
while((fp = fopen("shared.txt", "rt"))==NULL);
fseek(fp, -1, SEEK_END);
c = fgetc(fp);
fclose(fp);
return c;
}
void consumer(){
char c;
do{
c = readChar();
printf("This is the latest character supplied: %c\n", c);
}while(c!='x');
}
int main(){
int pid = fork(); //now we fork processes
if(pid ==0 ){
producer(); //the child process should run and create some text in the file
}else{
wait(); consumer();
}
}
我试图在调用它们各自的分支之后添加等待语句producer()
,consumer()
但基本上无论如何,程序都无法执行我想要的操作。如果main ()
我有
int main(){
int pid = fork(); //now we fork processes
if(pid ==0 ){
producer(); //the child process should run and create some text in the file
}else{
consumer();
}
}
我陷入了无限循环。在一个或两个分支中的函数调用之后添加wait();
无济于事,因为无限循环发生在控制传递给其中一个之前wait()
。
如果我试试这个:
int main(){
int pid = fork(); //now we fork processes
if(pid ==0 ){
producer(); //the child process should run and create some text in the file
}else{
wait(); consumer();
}
}
我可以输入文本,stdin
直到我输入'x'
,但正如预期的那样,消费者只读取写入文件的最后一个字符。
有没有办法让它与等待语句一起工作?
解决方案
问题在于并发
我会说问题在于(缺乏)同步。在生产者/消费者安排中,生产者通常有一种方法向消费者发出信号,告知消费者有新商品可供消费,消费者在尝试消费之前等待该信号。细节从那里有所不同,但它们通常还包括一种让生产者向消费者发出信号的方式,即不会有更多的物品出现。
您的消费者不会等待任何明确的信号,也不会使用可用的数据(文件长度)来确定新项目是否可用。另一方面,它不努力在消耗的物品中保持自己的位置,因此如果生产者领先于它,它很容易错过物品。此外,消费者忙于循环,执行昂贵的 I/O 操作,这是一种非常昂贵的方法。
有没有办法让它与等待语句一起工作?
仅当您希望生产者在消费者消费任何东西之前运行完成。就是这样wait()
做的:它等待另一个进程终止。在这种情况下,您可能希望消费者从头开始逐个字符地读取文件,而不是直接跳到最后。
如果您希望生产者和消费者同时取得进展,那么最简单的方法是利用系统已经提供给您的设施,通过使用 FIFO 或管道而不是常规文件。然后生产者可以一个接一个地写,而消费者可以一个接一个地读,没有任何重新打开和重新定位的废话。
如果您必须使用常规文件执行此操作,那么您可以使用一对信号量或互斥量 + 条件变量来让生产者和消费者轮流进行。或者,消费者可以通过多种方式监视文件以检测文件何时发生更改(stat
/ fstat
、inotify
等),以避免不必要地尝试从中读取,您可以将其与跟踪其在该文件中的位置相结合,以免重新读取它已经消耗的数据。理想情况下,两个程序都不会多次打开文件,但生产者可能需要fflush
在每次写入后打开文件。
推荐阅读
- java - Java 8 Streams 删除重复字母
- python - 我们可以在 gunicorn 中使用 python ThreadingHTTPServer 吗?
- c# - 将枚举转换为给定类型的列表
- python - 在 Python 中转储时缩进 YAML 列表项
- sql - 使用 Sequelize 和 Express 更新链接表
- android - 为什么使用 Xamarin Forms 的 iOS 应用程序不显示 Font Awesome?
- javascript - Parcel:将内容哈希添加到 html
- c++ - 未定义对具有抽象类的 vtable 的引用
- vscode-settings - VScode:预期的逗号 Jsonc
- swift - Swift - 使用 URLSessionStreamTask 使我的应用程序与 Firebase 数据库数据保持同步