• 多线程编程示例3(生产者消费者)


         生产者消费者问题是一个著名的线程同步问题,该问题描述如下:有一个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个具有多个缓冲区的缓冲池,生产者将它生产的产品放入一个缓冲区中,消费者可以从缓冲区中取走产品进行消费,显然生产者和消费者之间必须保持同步,即不允许消费者到一个空的缓冲区中取产品,也不允许生产者向一个已经放入产品的缓冲区中再次投放产品。

        这个生产者消费者题目不仅常用于操作系统的课程设计,也常常在程序员和软件设计师考试中出现。并且在计算机考研的专业课考试中也是一个非常热门的问题。因此现在就针对这个问题进行详细深入的解答。

        首先来简化问题,先假设生产者和消费者都只有一个,且缓冲区也只有一个。这样情况就简便多了。

        第一.从缓冲区取出产品和向缓冲区投放产品必须是互斥进行的。可以用关键段和互斥量来完成。

        第二.生产者要等待缓冲区为空,这样才可以投放产品,消费者要等待缓冲区不为空,这样才可以取出产品进行消费。并且由于有二个等待过程,所以要用二个事件或信号量来控制。

        以上这段分析见http://blog.csdn.net/morewindows/article/details/7577591,不能再赞了。

    以下为自己实现的代码:(多生产者未实现,不过可以参照多消费者,将生产者的数量放在临界区进行操作)

    #pragma once
    
    #define _CRTDBG_MAP_ALLOC
    #include<cstdio>
    #include<Windows.h>
    #include<crtdbg.h>
    #include<process.h>
    
    //设置控制台输出颜色
    BOOL SetConsolecolor(WORD wAttributes)
    {
        HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
        if (hConsole==INVALID_HANDLE_VALUE)
        {
            return false;
        }
        return SetConsoleTextAttribute(hConsole, wAttributes);
    }
    //1生产者 1消费者
    int buffer = 10; //缓冲区
    
    //1生产者 2消费者 4产品缓冲池
    bool BoolEnd=false;
    int buffer_ni = 0, buffer_nj = 0;
    const int buffer_size = 4;
    int buffer_multi[buffer_size]{0};
    
    const int produce_num = 10; //生产产品个数
    CRITICAL_SECTION cs;
    HANDLE g_hEventBufferEmpty, g_hEventBufferFull;
    HANDLE g_hSemaphoreBufferEmpty, g_hSemaphoreBufferFull;
    
    //生产者线程函数
    unsigned int _stdcall ProducerThreadFun(PVOID pM)
    {
        for (size_t i = 0; i < produce_num; i++)
        {
            //等待缓冲区为空
            WaitForSingleObject(g_hEventBufferEmpty, INFINITE);
    
            //互斥的访问缓冲区
            EnterCriticalSection(&cs);
            SetConsolecolor(FOREGROUND_RED);
            buffer = i;
            printf("生产者将第%2d件产品放入缓冲区
    ", i);
            LeaveCriticalSection(&cs);
    
            //通知缓冲区有数据
            SetEvent(g_hEventBufferFull);
        }
        return 0;
    }
    
    //消费者线程函数
    unsigned int _stdcall ConsumerThreadFun(PVOID pM)
    {
        volatile bool flag = true;
        while (flag)
        {
            //等待缓冲区有数据
            WaitForSingleObject(g_hEventBufferFull, INFINITE);
    
            //互斥的访问缓冲区
            EnterCriticalSection(&cs);
            SetConsolecolor(FOREGROUND_GREEN);
            printf("消费者从缓冲区提取第%d件产品
    ", buffer);
            if (produce_num == buffer + 1)
            {
                flag = false;
            }
            LeaveCriticalSection(&cs);
    
            //通知缓冲区为空
            SetEvent(g_hEventBufferEmpty);
    
            Sleep(10); //产品区为空,停止提取产品(避免空转),让生产者生产产品
        }
        return 0;
    }
    
    unsigned int _stdcall ProduceThreadFun_Multi(PVOID pM)
    {
        for (size_t i = 0; i < produce_num; i++)
        {
            //等待有空的缓冲区出现
            WaitForSingleObject(g_hSemaphoreBufferEmpty, INFINITE);
    
            //互斥的访问缓冲区
            EnterCriticalSection(&cs);
            SetConsolecolor(FOREGROUND_RED);
            buffer_multi[buffer_ni] = i;
            printf("生产者在缓冲池第%d个缓冲区投放数据%2d
    ", buffer_ni, i);
            buffer_ni = (buffer_ni + 1) % buffer_size;
            LeaveCriticalSection(&cs);
    
            //通知消费者有新数据
            ReleaseSemaphore(g_hSemaphoreBufferFull, 1, NULL);
        }
        printf("生产者完成任务,线程结束运行!
    ");
        return 0;
    }
    
    unsigned int _stdcall ConsumerThreadFun_Multi(PVOID pM)
    {
        while (true)
        {
            //等待非空的缓冲区出现
            WaitForSingleObject(g_hSemaphoreBufferFull, INFINITE);
    
            //互斥的访问缓冲区
            EnterCriticalSection(&cs);
            if (BoolEnd) //另外一个消费者线程已经读取最后一件产品
            {
                LeaveCriticalSection(&cs); break;
            }
            SetConsolecolor(FOREGROUND_GREEN);
            printf("  编号为%d的消费者从缓冲区中第%d缓冲区提取产品%2d
    ", GetCurrentThreadId(), buffer_nj, buffer_multi[buffer_nj]);
            if (buffer_multi[buffer_nj]+1==produce_num)
            {
                BoolEnd = true; //读取结束标识符
                LeaveCriticalSection(&cs);
                //读取最后一个产品,因为另外一个线程在等待,故添加信号量
                //这样会导致重复读取最后一件产品,可添加特殊标记,此处添加BoolEnd
                ReleaseSemaphore(g_hSemaphoreBufferFull, 1, NULL);
                break;
            }
            buffer_nj = (buffer_nj + 1) % buffer_size;
            LeaveCriticalSection(&cs);
    
            Sleep(50); //避免空转,让生产区填充产品
            ReleaseSemaphore(g_hSemaphoreBufferEmpty, 1, NULL);
        }
        SetConsolecolor(FOREGROUND_RED);
        printf("
    编号为%d的消费者收到通知,线程结束运行
    ", GetCurrentThreadId());
        return 0;
    }
    
    void fun1()
    {
        printf("生产者消费问题 1生产者 1消费者 
    ");
    
        //初始化临界段
        InitializeCriticalSection(&cs);
        //创建两个自动复位事件,注意参数区别
        g_hEventBufferEmpty = CreateEvent(NULL, FALSE, TRUE, NULL);
        g_hEventBufferFull = CreateEvent(NULL, FALSE, FALSE, NULL);
    
        const int threadnum = 2;
        HANDLE handle[threadnum];
    
        handle[0] = (HANDLE)_beginthreadex(NULL, 0, ProducerThreadFun, NULL, 0, NULL);
        handle[1] = (HANDLE)_beginthreadex(NULL, 0, ConsumerThreadFun, NULL, 0, NULL);
        WaitForMultipleObjects(threadnum, handle, TRUE, INFINITE);
    
        //销毁事件和关键段
        CloseHandle(handle[0]);
        CloseHandle(handle[1]);
        DeleteCriticalSection(&cs);
        CloseHandle(g_hEventBufferEmpty);
        CloseHandle(g_hEventBufferFull);
    }
    
    void fun2()
    {
        InitializeCriticalSection(&cs);
        g_hSemaphoreBufferEmpty = CreateSemaphore(NULL, 4, 4, NULL);
        g_hSemaphoreBufferFull = CreateSemaphore(NULL, 0, 4, NULL);
    
        const int thread_num = 3;
        HANDLE handle[thread_num];
    
        //初始化变量
        buffer_ni = 0;
        buffer_nj = 0;
        BoolEnd = false;
        memset(buffer_multi, 0, sizeof(buffer_multi));
    
        //生产者线程
        handle[0] = (HANDLE)_beginthreadex(NULL, 0, ProduceThreadFun_Multi, NULL, 0, NULL);
        //消费者线程
        handle[1] = (HANDLE)_beginthreadex(NULL, 0, ConsumerThreadFun_Multi, NULL, 0, NULL);
        handle[2] = (HANDLE)_beginthreadex(NULL, 0, ConsumerThreadFun_Multi, NULL, 0, NULL);
        WaitForMultipleObjects(thread_num, handle, TRUE, INFINITE);
        
        for (size_t i = 0; i < thread_num; i++)
        {
            CloseHandle(handle[i]);
        }
        CloseHandle(g_hSemaphoreBufferEmpty);
        CloseHandle(g_hSemaphoreBufferFull);
        DeleteCriticalSection(&cs);
    }
    
    int main()
    {
        //1生产者 1消费者
        //fun1();
    
        //1生产者 2消费者
        fun2();
    
        //检测内存泄漏
        _CrtDumpMemoryLeaks();
        return 0;
    }

    此题后续解决了双线程读取队列数据问题。

    详见:http://blog.csdn.net/morewindows/article/details/8646902

  • 相关阅读:
    第四章 处理器体系结构
    第四节、程序的机器语言
    第三节 信息的表示和处理
    app
    你只是看起来很努力
    tap news:week5 0.0 create react app
    28.week4
    ubuntu去除带锁文件的锁 sudo chown 用户名 目标文件夹/ -R
    26.如何使用python操作我们自己创建的docker image呢?
    25.week4 docker build 也就是创建自己的image 上传image到dockerhub 从dockerhub下载images
  • 原文地址:https://www.cnblogs.com/jason1990/p/4718296.html
Copyright © 2020-2023  润新知