Windows下生產者-消費者問題的解法

來源:互聯網
上載者:User

先來描述一下待解決的問題:

有一個倉庫,它最多有七個槽位,最開始每個槽位都是空的。當有空槽位的時候,允許生產者往裡面放東西。當槽位上有東西時,允許消費者從裡面拿東西;滿了則不允許再放,空了則不允許再拿。因為倉庫設計問題,同一時間內,只允許一個人進去放東西或者拿東西。需要盡最大的效率安排生產者和消費者的工作。可以看到,這裡生產和消費的動作不需要做嚴格的同步,只要規則允許,可以連續產生三次,也可以連續消費三次。而沒有說一定要生產一次,消費一次,再生產一次,再消費一次的順序來。

 

// 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是可以讓多個線程同時訪問共用資源的唯一訊號類型。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.