所以问题是这样的:
有两个进程,前台进程和后台进程。两者共享一个整数数组。前台进程通过终端接受整数,并将每个整数追加到共享数组中。后台进程在每次添加后将共享数组按升序排序。当最后一个元素放置在正确的位置时,前台进程将在终端上显示共享数组。
这是我的实现:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <semaphore.h>
#define MAX_BUFFER_SIZE 64
#define BUFFER_NAME "/shared"
struct data {
int buffer[MAX_BUFFER_SIZE];
sem_t mutex;
};
int foreground_process(int size)
{
int element;
int buff_fd = shm_open(BUFFER_NAME, O_RDWR, 0666);
struct data *D = mmap(NULL, sizeof(struct data), PROT_READ | PROT_WRITE, MAP_SHARED, buff_fd, 0);
sem_init(&(D->mutex), 7, 1);
for (int i = 0; i < size; ++i) {
sem_wait(&(D->mutex));
printf("Enter element %d: ", i);
scanf("%d", &element);
D->buffer[i] = element;
sem_post(&(D->mutex));
sleep(1);
}
sem_wait(&(D->mutex));
printf("Final array: ");
for (int i = 0; i < size; ++i)
printf("%d ", D->buffer[i]);
printf("\n");
sem_post(&(D->mutex));
close(buff_fd);
exit(0);
}
int background_process(int size)
{
int prev_element, tmp;
int buff_fd = shm_open(BUFFER_NAME, O_RDWR, 0666);
struct data *D = mmap(NULL, sizeof(struct data), PROT_READ | PROT_WRITE, MAP_SHARED, buff_fd, 0);
for (int i = 0; i < size; ++i) {
sem_wait(&(D->mutex));
prev_element = D->buffer[i];
printf("Got element %d at B[%d]!\n", D->buffer[i], i);
for (int j = 0; j < i; ++j) {
if (D->buffer[j] > prev_element) {
tmp = D->buffer[j];
D->buffer[j] = prev_element;
prev_element = tmp;
}
}
D->buffer[i] = prev_element;
sem_post(&(D->mutex));
sleep(1);
}
close(buff_fd);
exit(0);
}
int main(void) {
int size;
pid_t fg_pid, bg_pid;
pid_t wpid;
int status;
printf("Size: ");
scanf("%d", &size);
int buff_fd = shm_open(BUFFER_NAME, O_CREAT | O_RDWR, 0666);
ftruncate(buff_fd, sizeof(struct data));
if ((fg_pid = fork()) == 0) foreground_process(size);
else if (fg_pid < 0) perror("foreground process");
sleep(1);
if ((bg_pid = fork()) == 0) background_process(size);
else if (bg_pid < 0) perror("background process");
while ((wpid = wait(&status)) != - 1)
fprintf(stderr, "Process %d exists with status %d\n", wpid, WEXITSTATUS(status));
shm_unlink(BUFFER_NAME);
return 0;
}
字符串
要共享的数据对象是一个struct
,其中包含一个整数数组&一个二进制信号量(用于同步)。
这样就可以了:O/P示例:
Size: 5
Enter element 0: 2
Got element 2 at B[0]!
Enter element 1: 10
Got element 10 at B[1]!
Enter element 2: 1
Got element 1 at B[2]!
Enter element 3: 9
Got element 9 at B[3]!
Enter element 4: 8
Got element 8 at B[4]!
Final array: 1 2 8 9 10
Process 1849 exists with status 0
Process 1850 exists with status 0
型
但这不是一个合法的解决方案,因为我使用了3次sleep()
来实现这种来回同步。在fork后台和前台进程之间,确保前台进程首先运行。第二次和第三次,在前台和后台进程中的for
循环的每次迭代之后。
删除sleep()
会产生各种各样的问题,例如,后台进程似乎被mutex
饿死了,因此似乎无法运行等等,后台进程有时会在前台进程之前运行。
我能做些什么改进来解决这个问题吗?
3条答案
按热度按时间xoefb8l81#
sleep()
在线程或进程同步方案中没有合法的角色。相反,应使用适当的IPC机制,如互斥锁、信号量、条件变量和管道。如果不使用
sleep
,则无法可靠地执行此操作,原因如下:解决这个问题的方法不止一种,但我建议让父程序初始化信号量,就像它初始化共享内存一样,这样当其他程序启动时,它就已经准备好了。
sleep
不能解决这个问题,但它确实降低了一个进程递增信号量、循环然后再次递减信号量而不给另一个进程运行的可能性(否则很高)。如果你想用信号量来做这件事,那么使用 two。每个进程都在其中的一个进程上等待许可,并发布到另一个进程以让另一个进程继续。
6xfqseft2#
为了补充John's answer,这里是您的示例的修改版本,使用两个信号量在两个进程之间创建一个 lockstep。
请注意在父进程中如何初始化信号量,以及每个
sem_wait
和sem_post
的确切顺序。字符串
使用相同输入的结果:
型
9jyewag03#
下面是一个使用Ada编程语言的来回同步生产者-消费者任务的示例。
Ada提供了一种使用Rendezvous模型进行同步任务通信的机制。Ada任务使用一个或多个任务条目直接通信。任务条目可以将数据从调用任务传递到被调用任务,或者从被调用任务传递到调用任务。也可以在根本不传递任何数据的情况下实现任务条目。在这种情况下,任务调用充当简单的事件通知。
工作项目同步行程是由语言隐含行程。没有对任何信号量或互斥体的显式调用。当一个任务调用另一个任务的条目时,调用任务将被挂起,直到被调用任务接受该条目。类似地,如果被调用的任务遇到任务条目的接受语句,则该任务将被挂起,直到另一个任务调用该条目。当两个任务(调用和被调用)同时调用和接受相同的条目时,任何指定的数据都将从一个任务传输到另一个任务。在完成数据传输后,任务恢复其并发行为。
自Ada标准的1983年版本以来,同步任务通信一直是核心Ada语言的一个特征。
以下针对此问题中指定的问题的解决方案使用了通过两个任务条目进行通信的两个任务。
字符串
Nums_array型别是不受条件约束的数组型别,表示型别的任何特定执行严修在索引型别所施加的限制内,都可以有不同的长度。在本例中,索引类型为“正”。Positive是Integer的预定义子类型,最小值为1,最大值为Integer'Last,通常与C中的MAX_INT相同。
Main过程(也是Main任务或线程)声明一个名为Num_Elements的局部变量,它是Positive子类型的一个示例。
系统会提示使用者输入数组中的元素数目,并将回应指定给Num_Elements。
一个内部块从“declare”保留字开始。Ada要求在块、函数、过程、任务或包的声明性区域中声明所有类型、变量和常量。在此示例中,Nums_array的子类型被定义为索引范围从1开始到Num_Elements结束的数组类型。
后台任务定义有两个条目。
后台任务遍历Result数组,在每次迭代中接受add_element条目中的值。在接受来自前台任务的新值之后,对数组进行排序。当所有元素都已添加到数组中时,后台数组接受get_result条目,并将整个数组传递给前台任务。然后,后台任务终止。
主过程也是前台任务。因此,它会提示用户为所有Num_Elements数组元素输入值,每次都调用background.add_element并将输入得值传递给后台责任.完成输入循环后,主任务调用background.get_result并打印后台任务传递给它的数组值。
前台任务和后台任务之间的通信完全同步,而无需显式地操纵同步原语。
使用Ada编写的第二个版本实现了由前台任务和后台任务访问的共享缓冲区。共享缓冲区是使用Ada保护对象实现的。Ada保护的对象会自动受到保护,防止不适当的任务访问。
可以通过三种受保护的方法访问受保护的对象。这些方法是:
函数,这些函数对封装在受保护对象中的数据实现共享只读锁。共享只读锁允许多个任务同时从受保护对象读取数据,因为只读访问可防止出现任何争用情况。
过程,这些过程对封装在受保护对象中得数据实现无条件独占读写锁.一次只允许一个任务持有读写锁。
项,这些项对封装在受保护对象中的数据实现有条件的读写排他锁。每个条目都有一个关联的边界条件,该条件的值必须为TRUE才能访问受保护对象。当边界条件的值为FALSE时,调用任务被挂起并置于隐式实现的入口FIFO队列中,等待边界条件的值为TRUE。下列范例只会使用受保护的项目来控制对受保护对象中数组的存取,因此可确保前台工作只能在缓冲区没有新元素时写入缓冲区,而后台工作只能在缓冲区有新元素时排序数组。只有当数组处于完全排序状态时,前台任务才能获取共享数组的副本。
作为Ada 1995标准的一部分,Ada保护对象被添加到该语言中。
此解决方案的Ada程序代码为:
型