为了账号安全,请及时绑定邮箱和手机立即绑定

操作系统学习二:进程同步与互斥之生产者-消费者问题 .NetCore实现

标签:
Java

目的

1. 掌握进程(线程)的同步与互斥。

2. 掌握生产者消费者问题的实现方法。

3. 掌握VC的多线程编程方法。

内容

本实验要求设计并实现一个进程,该进程拥有3个生产者线程和1个消费者线程,它们使用10个不同的缓冲区。需要使用如下信号量:

l 一个mutex信号量,用以阻止生产者线程和消费者线程同时操作缓冲区队列;

l 一个full信号量,当生产者线程生产出一个物品时可以用它向消费者线程发出信号;

l 一个empty信号量,消费者线程释放出一个空缓冲区时可以用它向生产者线程发出信号;

生产者线程生产物品(通过等待一个时间模拟生产过程),然后将物品放置在一个空缓冲区中供消费者线程消费(通过将缓冲区数组元素值设为产品编号模拟放入过程)。消费者线程从缓冲区中获得物品,然后释放缓冲区(通过将缓冲区数组元素值设为0模拟取出过程)。当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来。

根据给出的相关函数,实现生产者消费者问题。为了分析结果,要求用文件保存缓冲区变化过程。

伪代码

下面给出的是解决这一问题的部分代码和伪代码,后面附上完整代码

#include <windows.h>    //大多数API函数都在这个头文件中定义#include <stdio.h>#include <iostream.h>const unsigned short SIZE_OF_BUFFER = 10;   //缓冲区个数 unsigned short ProductID = 0;     //生产的产品号unsigned short ConsumeID = 0;    //将被消耗的产品号unsigned short in = 0;         //产品进缓冲区时的缓冲区下标unsigned short out = 0;       //产品出缓冲区时的缓冲区下标//缓冲区是个循环队列int g_buffer[SIZE_OF_BUFFER];    //用整型数组模拟缓冲区bool  g_continue = true;      //当g_continue = false结束程序HANDLE g_hMutex;          // g_hMutex是互斥信号量的句柄         HANDLE g_hEmptySemaphore;     //即empty信号量的句柄HANDLE g_hFullSemaphore;     //即full信号量的句柄DWORD WINAPI Producer(LPVOID);    //生产者线程DWORD WINAPI Consumer(LPVOID);    //消费者线程//请完成主函数,主函数中要完成创建各信号量对象和创建各线程的工作main(…)
{    …         
//创建生产者和消费者线程CreateThread(…, Producer, …);
CreateThread(…, Consumer, …);
…
}//请完成模拟生产者把新生产的产品放入缓冲区的过程void AddToBuffer(){
    ...
}//请完成模拟消费者从缓冲区中取出一个产品的过程void TakeFromBuffer(){
…
}//请根据伪代码完成生产者线程函数Producer()
{
    …                  //获取线程ID
    while(TRUE)
    {   //模拟生产过程,生产一个物品
        sleep(rand);    //可以给出时间参数,1000为1秒,休眠时间 
        P(empty);                       //请求一个空缓冲区
        //操作缓冲区池
        P(mutex);
        …       //输出线程ID,这样可以看出是哪一个线程在工作
        AddToBuffer();  //将物品放置在一个空缓冲区中
        V(mutex);                   
        V(full);    //用信号通知一个满的缓冲区
    }   return 0;
}//请根据伪代码完成消费者线程函数       Consumer()
{
    …                  //获取线程ID
    while(TRUE)
    {       
        P(full);                            //请求一个满缓冲区
        //操作缓冲区池
        P(mutex);
…       //输出线程ID,这样可以看出是哪一个线程在工作
        TakeFromBuffer();  //从一个满缓冲区中取产品
        V(mutex);                       
        V(empty);   //用信号通知一个空的缓冲区
        sleep(rand);                    //休眠时间,模拟消费产品
    }    return 0;
}

实现代码

using System;using System.Dynamic;using System.Threading;namespace OperatingSystemExperiment.Exp2 {    /// <summary>
    /// 生产者-消费者问题
    /// 使用 P-V 操作解决同步和互斥问题
    /// 本实验要求设计并实现一个进程,该进程拥有3个生产者线程和1个消费者线程,它们使用10个不同的缓冲区。
    /// </summary>
    public static class Main {        private static int[] _buffer = new int[10];        /// <summary>
        /// 是否继续运行
        /// </summary>
        private static bool _continueRun = true;        /// <summary>
        /// 是否锁定缓冲区
        /// </summary>
        private static bool _isLock = false;        /// <summary>
        /// 产品号
        /// </summary>
        private static int _productId = 0;        /// <summary>
        /// 信号枚举
        /// </summary>
        private enum SemaphoreEnum {            /// <summary>
            /// 互斥信号量,用以阻止生产者线程和消费者线程同时操作缓冲区队列
            /// </summary>
            Mutex,            /// <summary>
            /// 当生产者线程生产出一个物品时可以用它向消费者线程发出信号
            /// </summary>
            Full,            /// <summary>
            /// 消费者线程释放出一个空缓冲区时可以用它向生产者线程发出信号
            /// </summary>
            Empty
        }        public static void Do()        {
            ThreadStart producer = () => {                while (_continueRun) {
                    Thread.Sleep(1200);                    // 请求空缓冲区
                    var emptyBufferId = GetEmptyBuffer();                    // 没有空缓冲区,继续等
                    if (emptyBufferId == -1) continue;                    // 缓冲区锁定,等待
                    if (_isLock) continue;
                    P(SemaphoreEnum.Empty);
                    P(SemaphoreEnum.Mutex);
                    Console.WriteLine("生产线程 {0} 工作中", Thread.CurrentThread.ManagedThreadId);
                    AddToBuffer(emptyBufferId, ++_productId);
                    Console.WriteLine("Produce the {0} product to buffer.", _productId);                    // 输出缓冲区内容
                    var nextIn = GetEmptyBuffer();                    var nextOut = GetFullBuffer();                    for (var i = 0; i < _buffer.Length; i++) {                        if (i == nextOut)
                            Console.WriteLine("{0}: {1} <- 下一个可取出产品消费的地方", i, _buffer[i]);                        else if (i == nextIn)
                            Console.WriteLine("{0}: {1} <- 可放下一个产品的位置", i, _buffer[i]);                        else
                            Console.WriteLine("{0}: {1}", i, _buffer[i]);
                    }

                    V(SemaphoreEnum.Mutex);                    // 用信号通知一个消费者线程有一个满的缓冲区
                    V(SemaphoreEnum.Full);
                }
            };            // 1个监视线程
            new Thread(() => {                while (_continueRun)
                {                    if (_productId > 20)
                        _continueRun = false;
                }
            }).Start();            // 3个生产者
            new Thread(producer).Start();            new Thread(producer).Start();            new Thread(producer).Start();            // 1个消费者
            new Thread(() => {                while (_continueRun) {
                    Thread.Sleep(200);                    // 请求一个满的缓冲区
                    var fullBufferId = GetFullBuffer();                    if (fullBufferId == -1) continue;                    // 缓冲区锁定则继续等待
                    if (_isLock) continue;
                    P(SemaphoreEnum.Full);                    // 操作缓冲区池
                    P(SemaphoreEnum.Mutex);
                    Console.WriteLine("消费者线程 {0} 工作", Thread.CurrentThread.ManagedThreadId);                    var productId = TakeFromBuffer(fullBufferId);
                    Console.WriteLine("正在消费产品 {0}", productId);
                    V(SemaphoreEnum.Mutex);                    // 用信号通知一个空的缓冲区
                    V(SemaphoreEnum.Empty);
                }
            }).Start();
        }        /// <summary>
        /// 生产者把新生产的产品放入缓冲区
        /// </summary>
        /// <returns>是否成功放入,没有空缓冲区的时候不成功</returns>
        private static bool AddToBuffer(int position, int product)        {            if (_buffer[position] != 0) return false;
            _buffer[position] = product;            return true;
        }        /// <summary>
        /// 获取一个空的缓冲区,都是满的则返回-1
        /// </summary>
        /// <returns>空缓冲区的编号</returns>
        private static int GetEmptyBuffer()        {            for (var i = 0; i < _buffer.Length; i++) {                if (_buffer[i] == 0) {                    return i;
                }
            }            return -1;
        }        /// <summary>
        /// 获取一个满的缓冲区,都是空的则返回-1
        /// </summary>
        /// <returns>满缓冲区的编号</returns>
        private static int GetFullBuffer()        {            for (var i = 0; i < _buffer.Length; i++) {                if (_buffer[i] != 0) {                    return i;
                }
            }            return -1;
        }        /// <summary>
        /// 消费者从缓冲区中取出一个产品
        /// </summary>
        /// <returns>产品id</returns>
        private static int TakeFromBuffer(int position)        {            var temp = _buffer[position];
            _buffer[position] = 0;            return temp;
        }        /// <summary>
        /// 申请资源操作
        /// </summary>
        /// <param name="s"></param>
        private static void P(SemaphoreEnum s)        {            switch (s) {                case SemaphoreEnum.Mutex:
                    _isLock = true;                    break;                case SemaphoreEnum.Full:                    break;                case SemaphoreEnum.Empty:                    break;                default:                    throw new ArgumentOutOfRangeException(nameof(s), s, null);
            }
        }        /// <summary>
        /// 释放资源操作
        /// </summary>
        /// <param name="s"></param>
        private static void V(SemaphoreEnum s)        {            switch (s) {                case SemaphoreEnum.Mutex:
                    _isLock = false;                    break;                case SemaphoreEnum.Full:                    break;                case SemaphoreEnum.Empty:                    break;                default:                    throw new ArgumentOutOfRangeException(nameof(s), s, null);
            }
        }
    }
}

运行结果

生产线程 4 工作中
Produce the 1 product to buffer.
0: 1 <- 下一个可取出产品消费的地方
1: 0 <- 可放下一个产品的位置
2: 0
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
消费者线程 7 工作
正在消费产品 1
生产线程 5 工作中
Produce the 2 product to buffer.
0: 2 <- 下一个可取出产品消费的地方
1: 0 <- 可放下一个产品的位置
2: 0
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
消费者线程 7 工作
正在消费产品 2
生产线程 4 工作中
Produce the 3 product to buffer.
0: 3 <- 下一个可取出产品消费的地方
1: 0 <- 可放下一个产品的位置
2: 0
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
消费者线程 7 工作
正在消费产品 3
生产线程 6 工作中
Produce the 4 product to buffer.
0: 4 <- 下一个可取出产品消费的地方
1: 0 <- 可放下一个产品的位置
2: 0
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
消费者线程 7 工作
正在消费产品 4
生产线程 4 工作中
Produce the 5 product to buffer.
0: 5 <- 下一个可取出产品消费的地方
1: 0 <- 可放下一个产品的位置
2: 0
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
消费者线程 7 工作
正在消费产品 5
生产线程 5 工作中
Produce the 6 product to buffer.
0: 6 <- 下一个可取出产品消费的地方
1: 0 <- 可放下一个产品的位置
2: 0
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
生产线程 4 工作中
Produce the 7 product to buffer.
0: 6 <- 下一个可取出产品消费的地方
1: 7
2: 0 <- 可放下一个产品的位置
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
消费者线程 7 工作
正在消费产品 6
消费者线程 7 工作
正在消费产品 7
生产线程 6 工作中
Produce the 8 product to buffer.
0: 8 <- 下一个可取出产品消费的地方
1: 0 <- 可放下一个产品的位置
2: 0
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
生产线程 5 工作中
Produce the 9 product to buffer.
0: 8 <- 下一个可取出产品消费的地方
1: 9
2: 0 <- 可放下一个产品的位置
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
生产线程 4 工作中
Produce the 10 product to buffer.
0: 8 <- 下一个可取出产品消费的地方
1: 9
2: 10
3: 0 <- 可放下一个产品的位置
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
消费者线程 7 工作
正在消费产品 8
消费者线程 7 工作
正在消费产品 9
消费者线程 7 工作
正在消费产品 10
生产线程 6 工作中
Produce the 11 product to buffer.
0: 11 <- 下一个可取出产品消费的地方
1: 0 <- 可放下一个产品的位置
2: 0
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
生产线程 4 工作中
Produce the 12 product to buffer.
0: 11 <- 下一个可取出产品消费的地方
1: 12
2: 0 <- 可放下一个产品的位置
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
消费者线程 7 工作
正在消费产品 11
消费者线程 7 工作
正在消费产品 12
生产线程 5 工作中
Produce the 13 product to buffer.
0: 13 <- 下一个可取出产品消费的地方
1: 0 <- 可放下一个产品的位置
2: 0
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
生产线程 6 工作中
Produce the 14 product to buffer.
0: 13 <- 下一个可取出产品消费的地方
1: 14
2: 0 <- 可放下一个产品的位置
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
生产线程 4 工作中
Produce the 15 product to buffer.
0: 13 <- 下一个可取出产品消费的地方
1: 14
2: 15
3: 0 <- 可放下一个产品的位置
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
消费者线程 7 工作
正在消费产品 13
消费者线程 7 工作
正在消费产品 14
消费者线程 7 工作
正在消费产品 15
生产线程 5 工作中
Produce the 16 product to buffer.
0: 16 <- 下一个可取出产品消费的地方
1: 0 <- 可放下一个产品的位置
2: 0
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
生产线程 6 工作中
Produce the 17 product to buffer.
0: 16 <- 下一个可取出产品消费的地方
1: 17
2: 0 <- 可放下一个产品的位置
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
生产线程 4 工作中
Produce the 18 product to buffer.
0: 16 <- 下一个可取出产品消费的地方
1: 17
2: 18
3: 0 <- 可放下一个产品的位置
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
消费者线程 7 工作
正在消费产品 16
消费者线程 7 工作
正在消费产品 17
消费者线程 7 工作
正在消费产品 18
生产线程 5 工作中
Produce the 19 product to buffer.
0: 19 <- 下一个可取出产品消费的地方
1: 0 <- 可放下一个产品的位置
2: 0
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
生产线程 4 工作中
Produce the 20 product to buffer.
0: 19 <- 下一个可取出产品消费的地方
1: 20
2: 0 <- 可放下一个产品的位置
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
消费者线程 7 工作
正在消费产品 19
消费者线程 7 工作
正在消费产品 20
生产线程 5 工作中
Produce the 21 product to buffer.
0: 21 <- 下一个可取出产品消费的地方
1: 0 <- 可放下一个产品的位置
2: 0
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
生产线程 4 工作中
Produce the 22 product to buffer.
0: 21 <- 下一个可取出产品消费的地方
1: 22
2: 0 <- 可放下一个产品的位置
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
消费者线程 7 工作
正在消费产品 21
生产线程 6 工作中
Produce the 23 product to buffer.
0: 23 <- 下一个可取出产品消费的地方
1: 22
2: 0 <- 可放下一个产品的位置
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0



作者:画星星高手
链接:https://www.jianshu.com/p/3fe2bf944766


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消