标题:操作系统综合课程设计——生产者与消费者
只看楼主
晓婷长月
Rank: 1
等 级:新手上路
帖 子:61
专家分:0
注 册:2013-6-4
 问题点数:0 回复次数:2 
操作系统综合课程设计——生产者与消费者
操作系统综合课程设计——生产者与消费者
综  合  课  程  设  计


用多进程同步方法解决生产者-消费者问题




院    系:                             

班    级:                              

小组成员:                              
                                            

时    间:                              

指导老师:                              





 

目    录


一、题目    1
二、设计目的    1
三、问题分析    1
四、设计要求    2
五、设计方案    2
5.1 问题分析    2
5.2 生产者-消费者问题算法伪代码(Pseudo-code)    2
六、流程图    4
七、运行结果    5
八、总结    6
九、参考文献    6
附件:源程序    6















一、题目
用多进程同步方法解决生产者-消费者问题


二、设计目的
现代操作系统引入并发程序设计技术之后,程序的执行不再是顺序的,封闭的。在多个进程并发运行的过程中,进程之间可能产生相互制约的关系,即竞争和协作。为了协调各进程有序正确的进行,就需要各进程间能相互通信。如果各进程之间不加以来控制,就会产生错误,如与时间有关的错误等。这就需要考虑进程之间的同步和互斥等问题。
操作系统中经典的“生产者—消费者”问题正反映了进程并发执行的这种关系。
本课程设计的任务在于,通过编写一个具体的有关操作系统进程同步互斥的经典问题,加强对操作系统实现进程间同步与互斥的机制的理解。同时培养提出问题、发现知识、使用工具、解决问题的能力。
具体地,要编制出一个程序,利用PV原语以及进程创建、同步、互斥、销毁等相关的系统调用来模拟“生产者—消费者”问题。
本次课程设计所要完成的就是对“生产者—消费者”问题的模拟,根据操作系统中并发进程、临界区、同步和互斥等基本概念及理论进行设计,采用C++语言实现,来对进程进行模拟同步和互斥的控制。
此次课程设计对深入理解操作系统中进程的同步和互斥问题,探求对进程控制方法的学习上有重大意义。


三、问题分析
在操作系统引入并发程序设计技术之后,程序的执行不再是顺序和封闭的,程序外部的顺序特性消失,程序与计算不再一一对应。于是人们引入进程来描述这种变化。而一组进程在执行的时间上是重叠的,即进程并发执行。在多个进程并发运行的过程中,进程之间可能是无关的,也可能是交互的。交互进程之间可能产生的关系,包括竞争和协作。
竞争和协作就会出现对统一资源进行操作,进而引入临界区的概念——并发进程中与共享变量有关的程序段称为临界区,共享变量所代表的资源成为临界资源。如不对临界区进行管理就会产生一些与时间有关的错误。在操作系统有多种方法可以实现对临界区、临界资源的管理。其中最基本的方法是设置相应的信号量和P、V操作。


四、设计要求
1、每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的全部内容,当前指针位置和生产者/消费者进程的标识符。
2、生产者和消费者各有两个以上。
3、多个生产者或多个消费者之间须有共享对缓冲区进行操作的函数代码。


五、设计方案
编制用多进程同步方法解决生产者-消费者的程序,并完成对进程进行模拟同步和互斥的控制。
5.1 问题分析
有多个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个具有多个缓冲区的缓冲池,生产者将它生产的产品放入一个缓冲区中,消费者可以从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个已经放入产品的缓冲区中再次投放产品。
  这个生产者消费者题目不仅常用于操作系统的课程设计,也常常在程序员和软件设计师考试中出现。并且在计算机考研的专业课考试中也是一个非常热门的问题。因此现在就针对这个问题进行详细深入的解答。
首先来简化问题,先假设生产者和消费者都只有一个,且缓冲区也只有一个。这样情况就简便多了。
第一。从缓冲区取出产品和向缓冲区投放产品必须是互斥进行的。可以用关键段和互斥量来完成。
第二。生产者要等待缓冲区为空,这样才可以投放产品,消费者要等待缓冲区不为空,这样才可以取出产品进行消费。并且由于有二个等待过程,所以要用二个事件或信号量来控制。

5.2 生产者-消费者问题算法伪代码(Pseudo-code)
生产者线程函数
unsigned int __stdcall ProducerThreadFun(PVOID pM)   
{
对生产产品个数建立一个循环
    for (int i = 1; i <= END_PRODUCE_NUMBER; i++)   
{
访问互斥的缓冲区
        生产者在缓冲池缓冲区中投放数据
        通知消费者有新数据了
    }
    生产者完成任务,线程结束运行");
    return 0;   
}

消费者线程函数
unsigned int __stdcall ConsumerThreadFun(PVOID pM)   
{   
    while (true)
{
等待非空的缓冲区出现
访问互斥的缓冲区
        消费者从缓冲池中缓冲区取出数据

        if (线程出现结束标志)
        {
通知其它消费者有新数据了(结束标志)
        }   
        Sleep(50);
    }
    消费者的线程结束运行
    return 0;   
}


主函数
{
初始化信号量,一个记录有产品的缓冲区个数,另一个记录空缓冲区个数
定义生产者线程和消费者线程
执行生产者线程函数
执行消费者线程函数
   
调用并执行WaitForMultipleObjects函数,等待多个内核对象,改变使它返回的那个内核对象的状态

    return 0;   
}


六、流程图
 

七、运行结果
 
(注:本图只是程序运行的一种结果)

此次试验,所涉及的程序程序共有4个缓冲区,消费者共计2个,生产者1个。
初始时,由生产者向缓冲区中投放数据。缓冲区共计4个,投放的数据共8个。当生产者在向缓冲池中投放数据的同时,消费者也在一方面消费着生产着生产的产品。当生产者的任务结束时,生产者的线程便结束运行,接下来,将只有一个进程——消费者进程将一直运行,直到消费者完全将缓冲池中的数据取出。当消费者将缓冲池中的数据完全取出时,缓冲池中不再有数据,这时,消费者的进程结束,程序停止运行。由于设计的消费者进程有两个,所以在消费者进程结束时,两个进程的结束有一个先后关系。
由于进程的运行具有动态性与并发性,所以在生产者向缓冲池中投放数据与消费者向缓冲池中取数据时,具备有随机型。同一个程序的运行,可能会有不同的运行结果。而上图,也是所设计的程序运行的很多种结果中的一种,不断的运行该程序,会得到不同的结果。由于时间有限,没有将不同运行结果的总数统计出来。


八、总结
此次试验研究的是用多进程同步方法解决生产者-消费者问题。
实验基本完成了此次实验的任务。实验结果展示了进程的特点。
试验程序中,当消费者线程被触发时,寻找非空缓冲区并判断该产品是否属于其消费,若属于则消费产品并释放信号量。直到生产者生产完8个产品,消费者消费完对应的产品,线程结束与运行,被关闭。
程序不足的地方就是所涉及的生产者就只有一个,未能达到预期的实验要求。这是以后在实验中要注意改进的地方,同时也是技术的学习上要进行改进的地方。


九、参考文献
《操作系统原理》,华中科技大学出版社,庞丽萍编著,第四版
《C程序设计 (第三版)》,清华大学出版社,谭浩强著

附件:源程序
说明:
程序调试过程中可能会出现的问题以及解决方法:

可能会出现的问题:error C2065: '_beginthreadex' : undeclared identifier

解决方法:将工程按照下面的方式进行设置后重新编译,问题就可以解决: Project->Settings->C/C++->Code Generation->Use run-time libray->Debug Multithread,或 Multithread,或 Debug Multithread DLL, 或 Multithread DLL都可以,即Use run-time library需要使用多线程的。

搜索更多相关主题的帖子: 消费者 生产者 操作系统 课程 
2013-06-16 02:24
晓婷长月
Rank: 1
等 级:新手上路
帖 子:61
专家分:0
注 册:2013-6-4
得分:0 
源程序代码
程序代码:
/*提示——程序调试过程中可能会出现的问题以及解决方法

可能会出现的问题:error C2065: '_beginthreadex' : undeclared identifier

解决方法:将工程按照下面的方式进行设置后重新编译,问题就可以解决: Project->Settings->
C/C++->Code Generation->Use run-time libray->Debug Multithread,或 Multithread,
或 Debug Multithread DLL, 或 Multithread DLL都可以,即Use run-time library需要使用多线程的。

*/

#include <stdio.h>  
#include <process.h>  
#include <windows.h>   

  
const int END_PRODUCE_NUMBER = 8;   //生产产品个数  
const int BUFFER_SIZE = 4;          //缓冲区个数  
int g_Buffer[BUFFER_SIZE];          //缓冲池  
int g_i, g_j;
//信号量与关键段  
CRITICAL_SECTION g_cs;  
HANDLE g_hSemaphoreBufferEmpty, g_hSemaphoreBufferFull;

 
//生产者线程函数  
unsigned int __stdcall ProducerThreadFun(PVOID pM)  
{  
    for (int i = 1; i <= END_PRODUCE_NUMBER; i++)  
    {
        //互斥的访问缓冲区,上锁
        EnterCriticalSection(&g_cs);  
        g_Buffer[g_i] = i;
        //printf("\n");
        printf("生产者在缓冲池第%d个缓冲区中投放数据%d", g_i, g_Buffer[g_i]);
        printf("\n");
        g_i = (g_i + 1) % BUFFER_SIZE;  
        LeaveCriticalSection(&g_cs);  
    
        //通知消费者有新数据了  
        ReleaseSemaphore(g_hSemaphoreBufferFull, 1, NULL);  
    }
    printf("\n");
    printf("生产者完成任务,线程结束运行");
    printf("\n");
    return 0;  
}


//消费者线程函数  
unsigned int __stdcall ConsumerThreadFun(PVOID pM)  
{  
    while (true)  
    {  
        //等待非空的缓冲区出现  
        WaitForSingleObject(g_hSemaphoreBufferFull, INFINITE); 

        //互斥的访问缓冲区  
        EnterCriticalSection(&g_cs);
        printf("\n");
        printf("注意了:编号为%d的消费者从缓冲池中第%d个缓冲区取出数据%d", GetCurrentThreadId(), g_j, g_Buffer[g_j]);
        printf("\n");

        if (g_Buffer[g_j] == END_PRODUCE_NUMBER)//结束标志  
        {  
            LeaveCriticalSection(&g_cs);  
            //通知其它消费者有新数据了(结束标志)  
            ReleaseSemaphore(g_hSemaphoreBufferFull, 1, NULL);  
            break;  
        }  
        g_j = (g_j + 1) % BUFFER_SIZE;  
        LeaveCriticalSection(&g_cs);  
    
        Sleep(50);
        ReleaseSemaphore(g_hSemaphoreBufferEmpty, 1, NULL);  
    }
    printf("请注意:编号为%d的消费者收到通知,线程结束运行", GetCurrentThreadId());
    printf("\n");
    return 0;  
}


int main()
{
    printf("\n");
    InitializeCriticalSection(&g_cs);  
    //初始化信号量,一个记录有产品的缓冲区个数,另一个记录空缓冲区个数.  
    g_hSemaphoreBufferEmpty = CreateSemaphore(NULL, 4, 4, NULL);  
    g_hSemaphoreBufferFull  = CreateSemaphore(NULL, 0, 4, NULL);  
    g_i = 0;  
    g_j = 0;  
    memset(g_Buffer, 0, sizeof(g_Buffer));  
    
    const int THREADNUM = 3;  
    HANDLE hThread[THREADNUM];  
    //生产者线程  
    hThread[0] = (HANDLE)_beginthreadex(NULL, 0, ProducerThreadFun, NULL, 0, NULL);
    hThread[3] = (HANDLE)_beginthreadex(NULL, 0, ProducerThreadFun, NULL, 0, NULL);
    //消费者线程  
    hThread[1] = (HANDLE)_beginthreadex(NULL, 0, ConsumerThreadFun, NULL, 0, NULL);  
    hThread[2] = (HANDLE)_beginthreadex(NULL, 0, ConsumerThreadFun, NULL, 0, NULL);  
   
    WaitForMultipleObjects(THREADNUM, hThread, TRUE, INFINITE);  
    for (int i = 0; i < THREADNUM; i++)  
        CloseHandle(hThread[i]);  
    
   
    return 0;  
}


2013-06-16 02:25
晓婷长月
Rank: 1
等 级:新手上路
帖 子:61
专家分:0
注 册:2013-6-4
得分:0 
附件:操作系统综合课程设计——生产者与消费者(包含全部文件)

课程设计_用多进程同步方法解决生产者-消费者问题.rar (124.19 KB)
2013-06-16 02:25



参与讨论请移步原网站贴子:https://bbs.bccn.net/thread-415261-1-1.html




关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.228701 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved