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>

【文尾】如果文章对您有帮助,请留下对我和蟹儿的祝福。如果需要源代码,请留下祝福以及您邮箱,我会在第一时间回复,包含此程序中的疑问,欢迎一起讨论



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值