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

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


一. 实验目的

(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来分别控制缓冲区是否已满或已空,从而实现了生产者和消费者之间的同步与互斥。

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

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

目录
相关文章
|
24天前
|
安全 Java
深入Java并发编程:线程同步与互斥机制
【4月更文挑战第6天】Java并发编程中,确保数据一致性与防止条件竞争是关键。语言提供`synchronized`关键字、`Lock`接口和原子变量等机制处理并发问题。线程同步问题包括竞态条件、死锁和活锁。`synchronized`实现内置锁,`Lock`接口提供更灵活的锁管理,原子变量则支持无锁安全操作。理解并恰当使用这些工具能有效管理并发,避免数据不一致。
|
2月前
|
消息中间件 存储 算法
【软件设计师备考 专题 】操作系统的内核(中断控制)、进程、线程概念
【软件设计师备考 专题 】操作系统的内核(中断控制)、进程、线程概念
84 0
|
2月前
|
Java 云计算
Java多线程编程中的同步与互斥机制探析
在当今软件开发领域,多线程编程是一项至关重要的技能。本文将深入探讨Java中的同步与互斥机制,分析其在多线程环境下的应用及实现原理,帮助读者更好地理解并运用这一关键技术。
24 4
|
2月前
|
存储 消息中间件 算法
《操作系统》——进程与线程
《操作系统》——进程与线程
|
2月前
|
Python
在Python中,如何保证多个线程之间的同步?
在Python中,如何保证多个线程之间的同步?
24 4
|
2月前
|
Python
如何在Python中实现线程之间的同步和通信?
【2月更文挑战第17天】【2月更文挑战第51篇】如何在Python中实现线程之间的同步和通信?
|
7天前
|
监控 安全 Java
一文讲明白Java中线程与进程、并发与并行、同步与异步
一文讲明白Java中线程与进程、并发与并行、同步与异步
7 1
|
28天前
|
安全 Java
Java中的并发编程:探索多线程同步与锁机制
在Java编程领域,多线程并发编程是一个重要而又复杂的话题。本文将深入探讨Java中的多线程同步与锁机制,包括synchronized关键字、ReentrantLock类以及java.util.concurrent包中的各种并发工具,帮助读者更好地理解和应用多线程编程技术。
8 1
|
2月前
|
传感器 安全 程序员
【C++多线程 同步机制】:探索 从互斥锁到C++20 同步机制的进化与应用
【C++多线程 同步机制】:探索 从互斥锁到C++20 同步机制的进化与应用
100 1
|
2月前
|
存储 编解码 算法
【ffmpeg音视频同步】解决ffmpeg音视频中多线程之间的数据同步问题
【ffmpeg音视频同步】解决ffmpeg音视频中多线程之间的数据同步问题
41 2