操作系统与网络 2019-3-7

1.生产者与消费者模式

1.1 给主对话框添加成员: LONG m_nProdCount (生产的总产品数)、 queue m_qProd (装产品的队列)、 bool m_bQuitFlag (退出线程的标记)、 HANDLE m_hEventBeginProd (开始生产的事件句柄)、 HANDLE m_hMutexProd (对于线程中需要用到的变量需要加锁,本次使用的是互斥量)、 HANDLE m_hSemaphoreCons (消费者开始消费的信号量)、 HANDLE m_hProduct[5] (生产者的句柄)、 HANDLE m_hCustomer[5] (消费者的句柄)、 static DWORD WINAPI ProductThreadProc(LPVOID lpParameter) , static DWORD WINAPI CustomerThreadProc(LPVOID lpParameter) (生产者消费者的线程处理函数)

public:
	LONG m_nProdCount;							// 总产品数
	queue<LONG> m_qProd;						// 装产品的队列
	bool m_bQuitFlag;							// 退出线程的falg
	HANDLE m_hEventBeginProd;					// 开始生产的事件
	HANDLE m_hMutexProd;						// 产品的锁
	HANDLE m_hSemaphoreCons;					// 消费者开始消费的信号

	HANDLE m_hProduct[5];
	HANDLE m_hCustomer[5];
	static DWORD WINAPI ProductThreadProc(LPVOID lpParameter);
	static DWORD WINAPI CustomerThreadProc(LPVOID lpParameter);

1.2 在构造函数中对上述的变量进行初始化

1.总产品数初始化为0: m_nProdCount = 0 ;
装产品的队列: m_qProd ;
退出线程的标记初始化为true: m_bQuitFlag = true ;
开始生产的事件: m_hEventBeginProd = 0 ;
产品的锁: m_hMutexProd = 0 ;
消费者开始消费的信号: m_hSemaphoreCons = 0 ;
初始化生产者和消费者的句柄数组: ::ZeroMemory(m_hProduct, sizeof(HANDLE)*5) , ::ZeroMemory(m_hCustomer, sizeof(HANDLE)*5) ;

CProdConsDlg::CProdConsDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CProdConsDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	m_nProdCount = 0;						// 总产品数
	m_qProd;								// 装产品的队列
	m_bQuitFlag = true;						// 退出线程的falg
	m_hEventBeginProd = 0;					// 开始生产的事件
	m_hMutexProd = 0;						// 产品的锁
	m_hSemaphoreCons = 0;					// 消费者开始消费的信号

	::ZeroMemory(m_hProduct, sizeof(HANDLE)*5);
	::ZeroMemory(m_hCustomer, sizeof(HANDLE)*5);
}

1.3 在 OnInitDialog 函数中创建所需要要的 事件 、 互斥量 、 信号量

1.创建一个人工复位起始无信号的事件: m_hEventBeginProd = ::CreateEvent(0, TRUE, FALSE, 0) ;
2.创建一个互斥量: m_hMutexProd = ::CreateMutex(0, FALSE, 0) ;
3.创建一个信号量: m_hSemaphoreCons = ::CreateSemaphore(0, 0, 100, 0) ;
4.用循环创建两个线程数组;

BOOL CProdConsDlg::OnInitDialog()
{
	... ...

	// 3-7 生产者消费者
	m_hEventBeginProd = ::CreateEvent(0, TRUE, FALSE, 0);
	if(m_hEventBeginProd == 0)
		return FALSE;
	m_hMutexProd = ::CreateMutex(0, FALSE, 0);
	if(m_hMutexProd == 0)
	{
		::CloseHandle(m_hEventBeginProd);
		m_hEventBeginProd = 0;
		return FALSE;
	}
	m_hSemaphoreCons = ::CreateSemaphore(0, 0, 100, 0);
	if (m_hSemaphoreCons == 0)
	{
		::CloseHandle(m_hEventBeginProd);
		m_hEventBeginProd = 0;
		::CloseHandle(m_hMutexProd);
		m_hMutexProd = 0;
		return FALSE;
	}
	for(int i=0; i<5; i++)
	{
		m_hProduct[i] = ::CreateThread(0, 0, &CProdConsDlg::ProductThreadProc, this, 0, 0);
	}
	for (int i=0; i<5; i++)
	{
		m_hCustomer[i] = ::CreateThread(0, 0, &CProdConsDlg::CustomerThreadProc, this, 0, 0);
	}

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

1.4 完成生产线程处理函数

1.首先等一个生产的事件,来事件的信号了再进入循环;
2.在生产产品的循环中,首先等待产品的互斥量,若可以进入则生产产品;
3.生产完产品后释放一个可以消费的信号量的信号;
4.将生产的产品号放到 List Box 控件上;

// 生产线程处理函数
DWORD WINAPI CProdConsDlg::ProductThreadProc(LPVOID lpParameter)
{
	CProdConsDlg* pThis = (CProdConsDlg*)lpParameter;
	LONG nID = 0;
	// 等生产的信号
	::WaitForSingleObject(pThis->m_hEventBeginProd, INFINITE); 
	
	while (pThis->m_bQuitFlag)
	{
		// =======================生产产品============================
		if(::WaitForSingleObject(pThis->m_hMutexProd, 10) == WAIT_TIMEOUT)
			continue;

		if(pThis->m_nProdCount >= 100)
		{
			::ReleaseMutex(pThis->m_hMutexProd);
			break;
		}
		pThis->m_nProdCount++;
		nID = pThis->m_nProdCount;
		pThis->m_qProd.push(pThis->m_nProdCount);

		::ReleaseMutex(pThis->m_hMutexProd);
		// =======================生产产品============================

		// =======================释放一个消费的信号==============================
		::ReleaseSemaphore(pThis->m_hSemaphoreCons, 1, 0);
		// =======================释放一个消费的信号==============================

		// =======================将产品号放到对话框上==============================
		CString str;
		str.Format(_T("%d"), nID);
		pThis->m_lsProd.AddString(str);
		// =======================将产品号放到对话框上==============================

	}
	return 0;
}

1.5 完成消费线程处理函数

1.直接进入消费循环,在消费循环中等待可以消费的信号量的信号;
2.进行消费,从产品队列中往下取产品号;
3.将消费了的产品号放到另一个 List Box 控件上;

// 消费线程处理函数
DWORD WINAPI CProdConsDlg::CustomerThreadProc(LPVOID lpParameter)
{
	CProdConsDlg* pThis = (CProdConsDlg*)lpParameter;
	int nID = 0;
	while (pThis->m_bQuitFlag)
	{
		// ======================等消费产品的信号=========================
		if(::WaitForSingleObject(pThis->m_hSemaphoreCons, 10) == WAIT_TIMEOUT)
		{
			continue;
		}
		// ======================等消费产品的信号=========================

		// ======================在队列中取产品========================
		::WaitForSingleObject(pThis->m_hMutexProd, INFINITE);
		if(pThis->m_qProd.empty() == true)
		{
			::ReleaseMutex(pThis->m_hMutexProd);
			continue;
		}
		nID = pThis->m_qProd.front();
		pThis->m_qProd.pop();
		::ReleaseMutex(pThis->m_hMutexProd);
		// ======================在队列中取产品========================

		// =====================放到对话框上=========================
		CString str;
		str.Format(_T("%d"), nID);
		pThis->m_lsCons.AddString(str);
		// =====================放到对话框上=========================
	}
	return 0;
}

1.6 给主对话框添加一个 OnClose 函数,在其中将所有的句柄等进行关闭

1.将线程退出标志变量置为false: m_bQuitFlag = false ;
2.循环关闭两个线程;
3.关闭三个句柄;

void CProdConsDlg::OnClose()
{
	// 删除句柄
	m_bQuitFlag = false;
	for(int i=0; i<5; i++)
	{
		if(::WaitForSingleObject(m_hProduct[i], 10) == WAIT_TIMEOUT)
			::TerminateThread(m_hProduct[i], -1);
		::CloseHandle(m_hProduct[i]);
		m_hProduct[i] = 0;
	}
	for(int i=0; i<5; i++)
	{
		if(::WaitForSingleObject(m_hCustomer[i], 10) == WAIT_TIMEOUT)
			::TerminateThread(m_hCustomer[i], -1);
		::CloseHandle(m_hCustomer[i]);
		m_hCustomer[i] = 0;
	}

	::CloseHandle(m_hEventBeginProd);
	m_hEventBeginProd = 0;
	::CloseHandle(m_hMutexProd);
	m_hMutexProd = 0;
	::CloseHandle(m_hSemaphoreCons);
	m_hSemaphoreCons = 0;

	CDialogEx::OnClose();
}

1.7 当我们点击 开始生产 按钮时,线程开始工作

1.在 开始生产 按钮中发一个事件信号;

void CProdConsDlg::OnBnClickedButton1()
{
	// 点击按钮
	::SetEvent(m_hEventBeginProd);
}

2.使用线程来重新完成任务管理器(游戏修改器)

1.首先我们考虑,只有在查找内存中的值时我们需要多个线程来同时进行可以节省时间,其他时候我们不需要多个线程来同时进行,因此我们需要改写 CModifyMemory 类

2.首先确定需要哪些变量

1.线程句柄: HANDLE m_hThread[4] ;
2.线程退出标记: bool m_bQuitThread ;
3.信号量的句柄: HANDLE m_hSemaphore (用来控制几个线程可以开始工作);
4.旋转锁: CMyLock m_lcAddr (用来锁线程中所公用的地址变量);
5.旋转锁: CMyLock m_lcList (用来锁线程中所公用的列表变量);
6.是否是第一次查找的标记: bool m_bFindFirstOrNot (第一次查找在内存中查找,第二次查找则在第一次查找的结果之上在列表中查找);
7.线程处理函数: static DWORD WINAPI ThreadProc(LPVOID lpParamter) ;
8.记录要查找的值: DWORD m_dwFindValue ;

// ======================用来锁地址或链表的=========================
class CMyLock
{
private:
	CRITICAL_SECTION cs;
public:
	CMyLock()
	{
		::InitializeCriticalSection(&cs);
	}
	~CMyLock()
	{
		::DeleteCriticalSection(&cs);
	}
	void MyLock()
	{
		::EnterCriticalSection(&cs);
	}
	void MyUnlock()
	{
		::LeaveCriticalSection(&cs);
	}
};
// ======================用来锁地址或链表的=========================

class CModifyMemory
{
public:
	// 重写为线程所需的成员
	HANDLE m_hThread[4];
	bool m_bQuitThread;
	HANDLE m_hSemaphore;				// 信号量
	CMyLock m_lcAddr;					// 锁地址
	CMyLock m_lcList;					// 锁链表
	bool m_bFindFirstOrNot;
	static DWORD WINAPI ThreadProc(LPVOID lpParamter);
	DWORD m_dwFindValue;				// 记录要查找的值
};

猜你喜欢

转载自blog.csdn.net/weixin_42896619/article/details/88430996