【操作系统】多线程同步与互斥

简介: 【操作系统】多线程同步与互斥


一. 实验目的

(1)加强对进程同步和互斥的理解,学会使用信号量解决资源共享问题。

(2)熟悉Linux 进程同步原语。

(3)掌握信号量wait/signal 原语的使用方法,理解信号量的定义、赋初值及wait/signal操作

二. 实验内容

(1)编写程序,使用Linux操作系统中的信号量机制模拟实现生产者-消费者问题。设有一个生产者和一个消费者,缓冲区可以存放产品,生产者不断生成产品放入缓冲区,消费者不断从缓冲区中取出产品,消费产品。

(2)以上实验只模拟了一个产品的放入与取出,请修改代码,以模拟实现多个产品的放入与取出。

三. 实验步骤

(1)编写程序,使用Linux操作系统中的信号量机制模拟实现生产者-消费者问题。设有一个生产者和一个消费者,缓冲区可以存放产品,生产者不断生成产品放入缓冲区,消费者不断从缓冲区中取出产品,消费产品。

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <semaphore.h>
#define SIZE 256
sem_t empty, full, mutex;
char *buffer;
void *producer()
{
    sem_wait(&empty); 
    sem_wait(&mutex); 
    printf("input something to buffer:\n");
    buffer = (char *)malloc(SIZE);
    fgets(buffer, SIZE, stdin);
    sem_post(&mutex); 
    sem_post(&full); 
}
void *consumer()
{
    sem_wait(&full); 
    sem_wait(&mutex);
    printf("read product from buffer: %s\n", buffer);
    memset(buffer, 0, SIZE);
    sem_post(&mutex); 
    sem_post(&empty); 
}
int main()
{
    pthread_t id_producer, id_consumer;
    int ret;
    sem_init(&empty, 0, 1); 
    sem_init(&full, 0, 0); 
    sem_init(&mutex, 0, 1); 
    ret = pthread_create(&id_producer, NULL, producer, NULL);
    ret = pthread_create(&id_consumer, NULL, consumer, NULL); 
    pthread_join(id_producer, NULL);
    pthread_join(id_consumer, NULL);
    sem_destroy(&empty);
    sem_destroy(&full);
    sem_destroy(&mutex);
    printf("The End ...\n");
    return 0;
}

该程序实现了生产者和消费者问题的解决方案,使用了三个信号量(empty、full和mutex)来控制生产和消费的进程访问缓冲区。

首先,程序定义了一个缓冲区(buffer),大小为256字节,用于生产者和消费者的数据交换。然后,使用了三个信号量来控制进程访问缓冲区的情况,其中,empty是缓冲区为空时的信号量,full是缓冲区为满时的信号量,mutex是用于保护缓冲区的互斥锁。

在producer函数中,生产者使用了P操作(sem_wait)操作empty和mutex信号量。如果empty信号量的值大于0,则表示缓冲区未满,可以向缓冲区中添加数据,否则就需要等待。同时,使用互斥锁mutex来保证缓冲区的同步访问。之后,生产者从标准输入中读取数据,将数据添加到缓冲区,并使用V操作(sem_post)操作mutex和full信号量,以此通知消费者缓冲区已经有数据可以读取。

在consumer函数中,消费者使用了P操作操作full和mutex信号量。如果full信号量的值大于0,则表示缓冲区有数据,可以读取,否则就需要等待。同样地,使用互斥锁mutex来保证缓冲区的同步访问。之后,消费者从缓冲区中读取数据,并将缓冲区清空。最后,使用V操作操作mutex和empty信号量,以此通知生产者缓冲区已经可写入新的数据。

在main函数中,创建了生产者和消费者进程,并等待它们完成。最后,使用sem_destroy函数来销毁信号量,并输出程序结束的信息。

总之,该程序使用了信号量来实现线程之间的同步,解决了生产者和消费者问题,保证了线程安全性和数据交换的可靠性。


(2)以上实验只模拟了一个产品的放入与取出,请修改代码,以模拟实现多个产品的放入与取出。

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <semaphore.h>
#define SIZE 256
sem_t empty, full, mutex;
char *buffer;
void *producer()
{
    while(1){
  sem_wait(&empty); 
  sem_wait(&mutex); 
  printf("input something to buffer:\n");
  buffer = (char *)malloc(SIZE);
  fgets(buffer, SIZE, stdin);
  sem_post(&mutex); 
  sem_post(&full); 
    }
}
void *consumer()
{
    while(1){
  sem_wait(&full); 
  sem_wait(&mutex);
  printf("read product from buffer: %s\n", buffer);
  memset(buffer, 0, SIZE);
  sem_post(&mutex); 
  sem_post(&empty); 
    }
}
int main()
{
    pthread_t id_producer, id_consumer;
    int ret;
    sem_init(&empty, 0, 1); 
    sem_init(&full, 0, 0); 
    sem_init(&mutex, 0, 1); 
    ret = pthread_create(&id_producer, NULL, producer, NULL);
    ret = pthread_create(&id_consumer, NULL, consumer, NULL); 
    pthread_join(id_producer, NULL);
    pthread_join(id_consumer, NULL);
    sem_destroy(&empty);
    sem_destroy(&full);
    sem_destroy(&mutex);
    printf("The End ...\n");
    return 0;
}

该程序是一个典型的生产者消费者问题的解决方案,使用了三个信号量(empty、full和mutex)来控制生产者和消费者访问缓冲区。不同之处在于,程序使用了while(1)循环,可以使生产者和消费者持续地向缓冲区中添加或读取数据,并避免在处理完一次数据后就退出的情况发生。

程序开始时,定义了一个缓冲区(buffer),大小为256字节,用于生产者和消费者的数据交换。接着,使用了三个信号量来控制进程访问缓冲区的情况,其中,empty是缓冲区为空时的信号量,full是缓冲区为满时的信号量,mutex是用于保护缓冲区的互斥锁。

在producer函数中,生产者使用了while(1)循环,表示生产者会一直运行下去。在循环中,生产者使用了P操作(sem_wait)操作empty和mutex信号量。如果empty信号量的值大于0,则表示缓冲区未满,可以向缓冲区中添加数据,否则就需要等待。同时,使用互斥锁mutex来保证缓冲区的同步访问。之后,生产者从标准输入中读取数据,将数据添加到缓冲区,并使用V操作(sem_post)操作mutex和full信号量,以此通知消费者缓冲区已经有数据可以读取。

在consumer函数中,消费者使用了while(1)循环,表示消费者会一直运行下去。在循环中,消费者使用了P操作操作full和mutex信号量。如果full信号量的值大于0,则表示缓冲区有数据,可以读取,否则就需要等待。同样地,使用互斥锁mutex来保证缓冲区的同步访问。之后,消费者从缓冲区中读取数据,并将缓冲区清空。最后,使用V操作操作mutex和empty信号量,以此通知生产者缓冲区已经可写入新的数据。

在main函数中,创建了生产者和消费者线程,并等待它们完成。最后,使用sem_destroy函数来销毁信号量,并输出程序结束的信息。

总之,该程序使用了信号量和互斥锁来实现线程之间的同步,解决了生产者和消费者问题,保证了线程安全性和数据交换的可靠性。使用while(1)循环可以使生产者和消费者持续地向缓冲区中添加或读取数据,并提高了程序的实用性。

四. 实验结果

(1)编写程序,使用Linux操作系统中的信号量机制模拟实现生产者-消费者问题。设有一个生产者和一个消费者,缓冲区可以存放产品,生产者不断生成产品放入缓冲区,消费者不断从缓冲区中取出产品,消费产品。

(2)以上实验只模拟了一个产品的放入与取出,请修改代码,以模拟实现多个产品的放入与取出。

五. 实验总结

本实验是通过对Linux操作系统中的信号量机制进行模拟实现,来深入理解多线程同步与互斥的概念和基本方法,同时也是对生产者-消费者模型的一个实际应用。

通过本次实验,我们深入了解了信号量的概念、使用方法以及相关的函数,并通过对生产者-消费者问题的模拟,掌握了相关的同步互斥机制。在实现过程中,我们使用了一个互斥信号量mutex来控制缓冲区和生产者、消费者之间的竞争,并使用了两个信号量full,empty来分别控制缓冲区是否已满或已空,从而实现了生产者和消费者之间的同步与互斥。

在实验中,我们还发现了一些问题和需要注意的地方。例如,在使用信号量时,需要保证每次操作都是原子操作,否则可能出现竞争条件,从而破坏了同步互斥机制。此外,还需要注意信号量的初始化以及销毁等问题,否则可能会出现资源泄漏等问题。

通过本次实验,我们不仅实现了生产者-消费者模型,并且深入理解了多线程同步与互斥的概念和方法,同时也锻炼了自己的编程能力和阅读和理解他人代码的能力。在今后的学习和工作中,我们将更好地应用信号量机制和多线程编程,并不断深化对操作系统和计算机原理的理解。

目录
相关文章
|
4天前
|
Unix 调度
操作系统(6)----线程相关
操作系统(6)----线程相关
10 0
|
4天前
|
关系型数据库 MySQL Java
实时计算 Flink版产品使用合集之mysql通过flink cdc同步数据,有没有办法所有表共用一个dump线程
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
8 0
|
4天前
|
缓存 算法 Java
操作系统(8)---进程的同步与互斥以及信号量机制(万字总结~)(4)
操作系统(8)---进程的同步与互斥以及信号量机制(万字总结~)
19 0
|
4天前
操作系统(8)---进程的同步与互斥以及信号量机制(万字总结~)(3)
操作系统(8)---进程的同步与互斥以及信号量机制(万字总结~)
14 0
|
4天前
|
C++ 调度
操作系统(8)---进程的同步与互斥以及信号量机制(万字总结~)(2)
操作系统(8)---进程的同步与互斥以及信号量机制(万字总结~)
13 0
|
4天前
|
算法 安全 调度
操作系统(8)---进程的同步与互斥以及信号量机制(万字总结~)(1)
操作系统(8)---进程的同步与互斥以及信号量机制(万字总结~)
15 0
操作系统(8)---进程的同步与互斥以及信号量机制(万字总结~)(1)
|
5天前
|
消息中间件 资源调度 安全
操作系统----临界区,临界资源,互斥量,互斥对象
操作系统----临界区,临界资源,互斥量,互斥对象
12 2
|
5天前
|
消息中间件 安全 Ubuntu
【操作系统原理】—— 线程同步
【操作系统原理】—— 线程同步
16 1
|
5天前
|
安全 C++
C++多线程编程:并发与同步
C++多线程编程:并发与同步
10 0
|
5天前
|
存储 Linux C语言
Linux:冯·诺依曼结构 & OS管理机制
Linux:冯·诺依曼结构 & OS管理机制
11 0