导航

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

Posted on 2011-11-30 08:58  hcfalan  阅读(374)  评论(0编辑  收藏  举报

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

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

 

// 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是可以让多个线程同时访问共享资源的唯一信号类型。