Windows核心編程手把手寫Queue程序

【文起】親愛的,謝謝你最近一直在關注租房信息。辛苦了寶貝兒,豆豆只能多看書來回報寶寶,愛你

                                                                                                            第九章 Queue程序筆記

程序運行情況:


1、Queue程序的目的是熟悉兩個知識點

a)互斥對象內核對象,包含共享資源

b)信標對象,標記當前可以使用的資源數。

2、我們創建了一個結構體

//每次寫入共享資源池中的值爲:1、第幾個線程;2、該線程的第幾次請求
struct ELENMENT
{
	DWORD dwProcessNum;//第幾個線程
	DWORD dwProcessRequestNum;//該線程的第幾次請求
};

typedef ELENMENT* PELENMENT;

3、再創建一個class,用來模擬服務器與客戶端之間的資源池

class CQueueList
{
public:
	CQueueList(int nMaxNum);
	~CQueueList();
	//模擬客戶端請求消息,給共享池添加數據
	BOOL AppendList(PELENMENT pElenment,DWORD dwTimeout);
	//模擬服務器處理請求消息,從共享池中讀出數據並移除之
	BOOL RemoveList(PELENMENT pElenment,DWORD dwTimeOut);
private:
	PELENMENT m_pElenments;
	int m_nMaxNum;//共享池最大可存多少請求
	HANDLE m_h[2];
	HANDLE &m_hMutex;//互斥內核對象
	HANDLE &m_hSemaphore;//信標兩個作用:1、共享池中是否有數據;2、共享池是否滿了
};

4、如下是CQueueList類的成員函數:

//初始化
CQueueList::CQueueList(int nMaxNum)
               :m_hMutex(m_h[0]),m_hSemaphore(m_h[1])
{
	m_nMaxNum = nMaxNum;
	m_pElenments = (PELENMENT)HeapAlloc(GetProcessHeap(),0,sizeof(ELENMENT) * nMaxNum);
	m_hMutex = CreateMutex(NULL,FALSE,NULL);
	m_hSemaphore = CreateSemaphore(NULL,0,nMaxNum,NULL);
}

CQueueList::~CQueueList()
{
	CloseHandle(m_hMutex);
	CloseHandle(m_hSemaphore);
	HeapFree(GetProcessHeap(),0,m_pElenments);
}

//模擬客戶端
BOOL CQueueList::AppendList(PELENMENT pElenment, DWORD dwTimeout)
{
	BOOL bOk = FALSE;
	//共享池是否沒其他線程使用
	DWORD dwType = WaitForSingleObject(m_hMutex,dwTimeout);
	if (WAIT_OBJECT_0 == dwType)
	{
		//可以使用該對象了,通過信標看看資源池是否滿,順便把上次資源池有多少線程也讀出來
		LONG lPreCount;
		bOk = ReleaseSemaphore(m_hSemaphore,1,&lPreCount);
		if (bOk)
		{
			//可以新增
			m_pElenments[lPreCount] = *pElenment;
		}
		else
		{
			//共享池滿了
			SetLastError(ERROR_DATABASE_FULL);
		}
		//操作結束了,釋放互斥內核對象
		ReleaseMutex(m_hMutex);
	}
	else
	{
		SetLastError(ERROR_TIMEOUT);
	}
	return bOk;
}

//模仿服務器
BOOL CQueueList::RemoveList(PELENMENT pElenment, DWORD dwTimeOut)
{
	//必須滿足兩個條件才能觸發服務器線程:1、共享池沒有其他線程使用;2、共享池有數據
	BOOL bOk = (WAIT_OBJECT_0 == WaitForMultipleObjects(2,m_h,TRUE,dwTimeOut));

	if (bOk)
	{
		//讀數據且後面的往前移
		*pElenment = m_pElenments[0];
		MoveMemory(&m_pElenments[0],&m_pElenments[1],sizeof(ELENMENT) * (m_nMaxNum - 1));

		//操作好了,釋放互斥對象,信標在Wait....函數中已經減一了
		ReleaseMutex(m_hMutex);
	}
	else
	{
		SetLastError(ERROR_TIMEOUT);
	}
	return bOk;
}


5、在對話框初始函數中,申請4個客戶端線程,2個服務器線程,爲了方便統計,我們定義了一些全局變量:

全局變量:

CQueueList g_QueueList(10);//共享池大小爲10
BOOL g_fShutDown = FALSE;

HANDLE g_hThreads[MAXIMUM_WAIT_OBJECTS];
int g_nThreadNums = 0;
啓動線程:

DWORD dwThread;
//啓動4個客戶端線程
for (int i = 1; i < 5;i++)
{
    g_hThreads[g_nThreadNums++] = 
    CreateThread(NULL,0,ClientThread,(LPVOID)(INT_PTR)i,0,&dwThread);
}
//啓動2個服務器線程
for (int i = 1; i < 3;i++)
{
    g_hThreads[g_nThreadNums++] = 
    CreateThread(NULL,0,ServerThread,(LPVOID)(INT_PTR)i,0,&dwThread);
}


6、服務器和客戶端線程函數:

DWORD WINAPI ClientThread(LPVOID pvParam)
{
	ULONG ulClientNum = PtrToLong(pvParam);
	HWND hwnd = FindWindow(NULL,_T("Queue"));
	hwnd = GetDlgItem(hwnd,IDC_LIST_CLIENT);

	for (int nRequest = 1;!g_fShutDown;nRequest++)
	{
		TCHAR sz[1024] = {0};
		ELENMENT e ={ulClientNum,nRequest};//存儲第幾個線程、第幾次請求

		if (g_QueueList.AppendList(&e,200))
		{
			//寫進入了
			wsprintf(sz,_T("Client %d Sending ResquestTime:%d"),ulClientNum,nRequest);
		}
		else
		{
			//沒寫進去,看看是超時還是滿了
			wsprintf(sz,_T("Client %d Sending ResquestTime:%d %s"),ulClientNum,nRequest,
				(ERROR_TIMEOUT == GetLastError())?_T("timeout"):_T("Full"));
		}
		ListBox_SetCurSel(hwnd,ListBox_AddString(hwnd,sz));
		Sleep(2500);//休息下
	}
	return 0;
}

DWORD WINAPI ServerThread(PVOID pvParam)
{
	ULONG ulServerNum = PtrToLong(pvParam);
	HWND hwnd = FindWindow(NULL,_T("Queue"));
	hwnd = GetDlgItem(hwnd,IDC_LIST_SERVER);
	if (1 == ulServerNum )
	{
		ListBox_SetCurSel(hwnd,ListBox_AddString(hwnd,_T("服務器豆漿已經啓動......")));
	}
	else
	{
		ListBox_SetCurSel(hwnd,ListBox_AddString(hwnd,_T("服務器蟹蟹已經啓動......")));
	}

	while (!g_fShutDown)
	{
		TCHAR sz[1024] = {0};
		ELENMENT e;
		if (g_QueueList.RemoveList(&e,5000))
		{
			//有數據了,讀出來
			wsprintf(sz,_T("Server:%d Done Client %d RequestTime %d"),ulServerNum,
				e.dwProcessNum,e.dwProcessRequestNum);
			Sleep(2000 * e.dwProcessNum);//需要時間去處理客戶端請求
		}
		else
		{
			wsprintf(sz,_T("Server:%d TimeOut"),ulServerNum);
		}
		ListBox_SetCurSel(hwnd,ListBox_AddString(hwnd,sz));
	}
	return 0;
}

7、退出窗口時,需要把申請的這些線程都Close掉,調用WM_DESTORY消息的處理函數

void CQueueDlg::OnDestroy()
{
	while (g_nThreadNums--)
	{
		CloseHandle(g_hThreads[g_nThreadNums]);
	}
	CDialog::OnDestroy();

	// TODO: 在此處添加消息處理程序代碼
}

如此,這個程序巧妙的運用了互斥對象內核對象以及信標對象。

需要添加的頭文件是:

#include <WindowsX.h>
#include <process.h>

【文尾】如果文章對您有幫助,請留下對我和蟹兒的祝福。如果需要源代碼,請留下祝福以及您郵箱,我會在第一時間回覆,包含此程序中的疑問,歡迎一起討論



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章