• Windows下生产者消费者问题的解法


    先来描述一下待解决的问题:

    有一个仓库,它最多有七个槽位,最开始每个槽位都是空的。当有空槽位的时候,允许生产者往里面放东西。当槽位上有东西时,允许消费者从里面拿东西;满了则不允许再放,空了则不允许再拿。因为仓库设计问题,同一时间内,只允许一个人进去放东西或者拿东西。需要尽最大的效率安排生产者和消费者的工作。可以看到,这里生产和消费的动作不需要做严格的同步,只要规则允许,可以连续生成三次,也可以连续消费三次。而没有说一定要生产一次,消费一次,再生产一次,再消费一次的顺序来。

    // productor_consumer.cpp
    // Windows 平台对生产者-消费者问题的解决方案

    #include <windows.h>
    #include <tchar.h>
    #include <list>

    // 定义仓库的最大槽位
    #define WH_SLOT    7

    // 定义等待信号灯的超时时间
    #define TIMEOUT    3000

    // 定义仓库类
    class Warehource
    {
    public:
        Warehource()
        {
            // 创建一个访问仓库的互斥量,生产者和消费者不能同时访问仓库
            m_hAccess = CreateEvent(NULL, FALSE, TRUE, NULL);
        }
        ~Warehource()
        {
            // 关闭访问互斥量
            CloseHandle(m_hAccess);
        }

        // 放入到仓库
        BOOL put(char ch)
        {
            // 尝试获取互斥量
            if (WAIT_OBJECT_0 == WaitForSingleObject(m_hAccess, 100))
            {
                m_slot.push_back(ch);
                printf("Product an item, now warehourse size is %d\n", m_slot.size());
                SetEvent(m_hAccess); //let's next access available
                return TRUE;
            }
            return FALSE;
        }

        // 从仓库取出
        BOOL get(char* ch)
        {
            // 尝试获取互斥量
            if (WAIT_OBJECT_0 == WaitForSingleObject(m_hAccess, 100))
            {
                *ch = m_slot.front();
                m_slot.pop_front();
                printf("Consume an item, now warehourse size is %d\n", m_slot.size());
                SetEvent(m_hAccess); //let's next access available
                return TRUE;
            }
            return FALSE;
        }
    private:
        std::list<char> m_slot;
        HANDLE m_hAccess; //对仓库访问要互斥
    };

    DWORD WINAPI ProductorRun(LPVOID ud);
    DWORD WINAPI ConsumerRun(LPVOID ud);

    HANDLE g_hProdExit;            //生产者退出的信号
    HANDLE g_hFreeSemaphore;    //仓库空闲槽的信号灯,最大值为WH_SLOT

    HANDLE g_hConsExit;            //消费者退出的信号
    HANDLE g_hDataSemaphore;    //仓库可用槽的信号灯,最大值为WH_SLOT

    int _tmain(int argc, _TCHAR* argv[])
    {
        Warehource wh;
        g_hProdExit = CreateEvent(NULL, FALSE, FALSE, NULL);
        g_hConsExit = CreateEvent(NULL, FALSE, FALSE, NULL);
        //仓库空闲槽的信号灯,最大值为WH_SLOT,初始值为WH_SLOT,因为仓库
        //一开始都是空的
        g_hFreeSemaphore = CreateSemaphore(NULL, WH_SLOT, WH_SLOT, NULL);
        //仓库可用槽的信号灯,最大值为WH_SLOT,初始值为0,因为仓库
        //一开始都是空的
        g_hDataSemaphore = CreateSemaphore(NULL, 0, WH_SLOT, NULL);

        // 创建生产者和消费者
        HANDLE hProductor = CreateThread(NULL, NULL, ProductorRun, &wh, 0, 0);
        HANDLE hConsumer  = CreateThread(NULL, NULL, ConsumerRun, &wh, 0, 0);

        // let main thread sleep 30 seconds
        Sleep(3000);

        // 结束生产者
        SetEvent(g_hProdExit);
        WaitForSingleObject(hProductor, INFINITE);
        CloseHandle(g_hProdExit);
        CloseHandle(hProductor);

        // 结束消费者
        SetEvent(g_hConsExit);   
        WaitForSingleObject(hConsumer, INFINITE);
        CloseHandle(g_hConsExit);
        CloseHandle(hConsumer);
        // 关闭信号灯
        CloseHandle(g_hFreeSemaphore);
        CloseHandle(g_hDataSemaphore);
        return 0;
    }

    DWORD WINAPI ProductorRun(LPVOID ud)
    {
        BOOL fDone = FALSE;
        HANDLE h2[] = { g_hProdExit, g_hFreeSemaphore };
        DWORD dwWait = 0;

        Warehource* wh = (Warehource*)ud;
        while (!fDone)
        {
            // 等待退出信号或空闲信号
            dwWait = WaitForMultipleObjects(2, h2, FALSE, TIMEOUT);
            if (dwWait == WAIT_OBJECT_0)
            {
                //退出信号
                fDone = TRUE;
            }
            else if (dwWait == WAIT_OBJECT_0+1)
            {
                // 空闲信号,表示仓库中有空闲槽,尝试放入到仓库
                if (wh->put('a'))
                {                
                    // 已经成功放入一个到仓库,则置上一个可用槽的信号灯
                    ReleaseSemaphore(g_hDataSemaphore, 1, NULL);
                }
                else //访问仓库超时
                {
                    // 将熄灭的空闲槽信号灯置回去
                    ReleaseSemaphore(g_hFreeSemaphore, 1, NULL);
                }
            }
        }

        return 0;
    }

    DWORD WINAPI ConsumerRun(LPVOID ud)
    {
        BOOL fDone = FALSE;
        HANDLE h2[] = { g_hConsExit, g_hDataSemaphore };
        DWORD dwWait = 0;

        Warehource* wh = (Warehource*)ud;
        while (!fDone)
        {
            // 等待退出信号或可用信号
            dwWait = WaitForMultipleObjects(2, h2, FALSE, TIMEOUT);
            if (dwWait == WAIT_OBJECT_0)
            {
                //退出信号
                fDone = TRUE;
            }
            else if (dwWait == WAIT_OBJECT_0+1)
            {
                // 可用信号,表示仓库中某个槽已有数据,尝试从中取用
                char ch;
                if (wh->get(&ch))
                {                
                    // 已经成功从仓库取出一个,则置上一个空闲槽的信号灯
                    ReleaseSemaphore(g_hFreeSemaphore, 1, NULL);
                }
                else //访问仓库超时
                {
                    // 将熄灭的可用槽信号灯置回去
                    ReleaseSemaphore(g_hDataSemaphore, 1, NULL);
                }
            }
        }
        return 0;
    }

    注意semaphore是可以让多个线程同时访问共享资源的唯一信号类型。

  • 相关阅读:
    搜狗图片抓取,主要以图集类进行抓取
    使用mongo进行去重操作
    爬虫实战01_淘宝模拟登录
    Linux常用命令02(远程管理)
    Linux常用命令01(文件和目录)
    Linux终端命令格式
    常用Linux命令的基本使用
    闭包与私有变量
    三栏布局的几种方法
    redux和react-redux小记
  • 原文地址:https://www.cnblogs.com/hcfalan/p/2268548.html
Copyright © 2020-2023  润新知