《Windows核心編程》第九章——用內核對象進行線程同步


先舉一個有bug的例子:

#include <iostream>
#include <windows.h>
#include <process.h>

using namespace std;
#define MAX_SIZE 0x500

HANDLE g_hSubmit;
HANDLE g_hReturn;
HANDLE g_hStop;
char g_szInput[MAX_SIZE] = {0};



unsigned int _stdcall ThreadServer(void* pParam)
{
    while (TRUE)
    {
        int nWaitRes = WaitForSingleObject(g_hStop, 100);
        if (WAIT_OBJECT_0 == nWaitRes)
            break;
        

        WaitForSingleObject(g_hSubmit, INFINITE);
        printf("Recieve:%s\n", g_szInput);
        SetEvent(g_hReturn);
    }
    SetEvent(g_hStop);
    printf("Set stop\n");
    return 0;
}

void main()
{
    int count = 0;
    g_hSubmit = CreateEvent(NULL, FALSE, FALSE, NULL);
    g_hReturn = CreateEvent(NULL, FALSE, FALSE, NULL);
    g_hStop = CreateEvent(NULL, FALSE, FALSE, NULL);

    HANDLE hTheadServer = (HANDLE)_beginthreadex(NULL, 0, ThreadServer, NULL, 0, NULL);
    while (TRUE)
    {
        count++;
        printf("Input:");
        cin.getline(g_szInput, MAX_SIZE);
        SetEvent(g_hSubmit);
        WaitForSingleObject(g_hReturn, INFINITE);
        if (count == 2){
            Sleep(2000);
            SetEvent(g_hStop);
            
            break;
        }
    }

    HANDLE hHandle[3];
    hHandle[0] = g_hStop;
    hHandle[1] = hTheadServer;
    WaitForMultipleObjects(2, hHandle, TRUE, INFINITE);
    CloseHandle(hTheadServer);
    CloseHandle(g_hReturn);
    CloseHandle(g_hStop);
    CloseHandle(g_hSubmit);
    getchar();

}

起初,我想要設置一個事件——g_hStop來通知線程,使得ThreadServer線程能夠被主線程停止,但是這里出現了一個問題,如果我刻意讓主線程Sleep2秒再去SetEvent,那么等待g_hStop的wait函數就會超時,從而往下繼續執行等待Input,而此時主線程已經退出input循環,那么就會死鎖。所以我改為使用全局變量來使得Threadserver線程退出:

#include <iostream>
#include <windows.h>
#include <process.h>

using namespace std;
#define MAX_SIZE 0x500

HANDLE g_hSubmit;
HANDLE g_hReturn;
HANDLE g_hStop;
int g_nCount = 0;
char g_szInput[MAX_SIZE] = {0};



unsigned int _stdcall ThreadServer(void* pParam)
{
    while (TRUE)
    {
        if (g_nCount == 2)
            break;

        WaitForSingleObject(g_hSubmit, INFINITE);
        printf("Recieve:%s\n", g_szInput);
        SetEvent(g_hReturn);
    }
    SetEvent(g_hStop);
    printf("Set stop\n");
    return 0;
}

void main()
{
    g_hSubmit = CreateEvent(NULL, FALSE, FALSE, NULL);
    g_hReturn = CreateEvent(NULL, FALSE, FALSE, NULL);
    g_hStop = CreateEvent(NULL, FALSE, FALSE, NULL);

    HANDLE hTheadServer = (HANDLE)_beginthreadex(NULL, 0, ThreadServer, NULL, 0, NULL);
    while (TRUE)
    {
        g_nCount++;
        printf("Input:");
        cin.getline(g_szInput, MAX_SIZE);
        SetEvent(g_hSubmit);
        WaitForSingleObject(g_hReturn, INFINITE);
        if (g_nCount == 2){
            Sleep(2000);
            SetEvent(g_hStop);
            
            break;
        }
    }

    HANDLE hHandle[2];
    hHandle[0] = g_hStop;
    hHandle[1] = hTheadServer;
    WaitForMultipleObjects(2, hHandle, TRUE, INFINITE);
    CloseHandle(hTheadServer);
    CloseHandle(g_hReturn);
    CloseHandle(g_hSubmit);
    getchar();

}

 

三種方式改進:

1、如果你非要使用第一種情況,那么請把等待時間設置的長一些,不要是100毫秒,起碼要是等待十秒,確保事件觸發后,不會超時。

2、在代碼設計的時候,就不要在while中使用兩個waitforsingleobject,這種設計就給死鎖帶來了可能性:

 

3、使用waitformultiobject等待兩個event之一,然后判斷等到的是哪個,從而決定來做什么

 

 

一個Mutex和semaphore合用的例子:

#include <iostream>
#include <windows.h>
#include <vector>
#include <process.h>

using namespace std;

#define MAX_SIZE 10

long g_ServerCount = 0;

class CQueue{
public:
    CQueue();
    ~CQueue();

    void Append();
    void Remove();
private:
    vector<int> m_vecQueue;
    HANDLE m_hMutex;
    HANDLE m_hSem;
    HANDLE m_hHandles[2];
};

CQueue g_c;

CQueue::CQueue()
{
    m_hMutex = CreateMutex(NULL, FALSE, NULL);
    m_hSem = CreateSemaphore(NULL, 0, 10, NULL);
    m_hHandles[0] = m_hMutex;
    m_hHandles[1] = m_hSem;
}

CQueue::~CQueue()
{
    CloseHandle(m_hMutex);
    CloseHandle(m_hSem);
}

void CQueue::Append(){
    DWORD dwRet = WaitForSingleObject(m_hMutex, INFINITE);
    InterlockedExchangeAdd(&g_ServerCount, 1);
    if (dwRet == WAIT_OBJECT_0)
    {
        LONG lPrevCount;
        int bRet = ReleaseSemaphore(m_hSem, 1, &lPrevCount);
        if (bRet)
        {
            m_vecQueue.push_back(g_ServerCount);
            printf("Add element %d\n", g_ServerCount);
        }
    }
    ReleaseMutex(m_hMutex);
}

void CQueue::Remove()
{
    DWORD dwRet = WaitForMultipleObjects(2, m_hHandles, TRUE, INFINITE);
    if (WAIT_OBJECT_0 == dwRet)
    {
        printf("Remove element %d\n", m_vecQueue.back());
        m_vecQueue.pop_back();
    }
    ReleaseMutex(m_hMutex);
}


unsigned int _stdcall ServerThread(void* pParam)
{
    while (TRUE)
    {
        Sleep(20);
        g_c.Append();
    }
    
    return 0;
}

unsigned int _stdcall ClientThread(void* pParam)
{
    while (TRUE)
    {
        Sleep(20);
        g_c.Remove();
    }
    
    return 0;
}

void main()
{
    HANDLE h_Handles[3];
    h_Handles[0] = (HANDLE)_beginthreadex(NULL, 0, ServerThread, NULL, 0, NULL);
    h_Handles[1] = (HANDLE)_beginthreadex(NULL, 0, ServerThread, NULL, 0, NULL);
    h_Handles[2] = (HANDLE)_beginthreadex(NULL, 0, ClientThread, NULL, 0, NULL);
    WaitForMultipleObjects(_countof(h_Handles), h_Handles, TRUE, INFINITE);
    for (int i = 0; i < _countof(h_Handles); i++)
        CloseHandle(h_Handles[i]);
    getchar();
}

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com