zoukankan      html  css  js  c++  java
  • 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是可以让多个线程同时访问共享资源的唯一信号类型。

  • 相关阅读:
    cf1100 F. Ivan and Burgers
    cf 1033 D. Divisors
    LeetCode 17. 电话号码的字母组合
    LeetCode 491. 递增的子序列
    LeetCode 459.重复的子字符串
    LeetCode 504. 七进制数
    LeetCode 3.无重复字符的最长子串
    LeetCode 16.06. 最小差
    LeetCode 77. 组合
    LeetCode 611. 有效三角形个数
  • 原文地址:https://www.cnblogs.com/hcfalan/p/2268548.html
Copyright © 2011-2022 走看看