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

1.继续线程池

1.1 将线程池中任务队列使用的STL容器更改为自己写的容器

1.我们自己定义一个循环队列,用这个循环队列来存储任务指针,起始时定义总长度为最多可存储元素个数+1(多出来的一个元素不做存储),头指针指向-1位置,尾指针指向0位置,向链表中添加元素则将尾指针 ++ ,当存满时(即尾指针指向最后一个元素),将尾指针重置为0;向链表中删除元素时,将头指针 ++
2.添加一个循环队列类: class CQueue ;向类中添加成员变量: int m_nBegin (头指针); int m_nEnd (尾指针); int m_nSize (容器中的元素个数); CTask** m_pArrTask (存储任务的指针数组);(本次使用int类型来表示头尾指针,因为定义的任务队列是一个指针数组,可以直接通过索引下标来进行任务指针的查找);
3.在构造函数中初始化: m_nBegin = -1 ; m_nEnd = 0 ; m_nSize = 0 ; m_pArrTask = new CTask*[SIZE_BUFFER] ; ::ZeroMemory(m_pArrTask, SIZE_BUFFERsizeof(CTask)) (SIZE_BUFFER 是定义的宏,定义为 1000+1);
4.在析构函数中重置,并删除指针数组: m_nBegin = -1 ; m_nEnd = 0 ; m_nSize = 0 ; delete[] m_pArrTask ; m_pArrTask = 0 ;
5.添加成员函数: bool my_push(CTask* pTask) (添加元素)、 void my_pop() (删除元素)、 bool my_isempty() (是否为空)、 CTask* my_front() (取头元素);
6.在添加元素函数中,首先判断输入参数是否是空或者队列是否满了,若是则返回false;通过下标索引添加元素: m_pArrTask[m_nEnd] = pTask ;将 m_nSize 与 m_nEnd 进行自加操作;再判断自加后的 m_nEnd 是否超过了最大存储范围;判断是否是第一次添加元素,若是则将 m_nBegin 加 1 ;
7.在删除元素函数中,首先判断队列中有没有元素,没有的话需要将 m_nBegin 和 m_nEnd 都重置为初始值;在头部删除: m_pArrTask[m_nBegin] = 0 ;将 m_nBegin 减 1 ,将 m_nSize 加 1 ;再判断加 1 后的 m_nBegin 是否超出了队列的最大存储范围,超出了的话就将其置为 0 ;
8.判断是否为空函数,直接判断 m_nSize 的值即可;
9.取头元素的函数,直接将 m_nBegin 位置的元素返回即可;

// 用自己写的容器来替换STL容器
#define SIZE_BUFFER 1000+1
class CQueue
{
private:
	int m_nBegin;					// 头尾
	int m_nEnd;
	int m_nSize;
	CTask** m_pArrTask;				// 任务数组

public:
	CQueue()
	{
		m_nBegin = -1;					// 头尾
		m_nEnd = 0;
		m_nSize = 0;
		m_pArrTask = new CTask*[SIZE_BUFFER];				// 任务数组
		::ZeroMemory(m_pArrTask, SIZE_BUFFER*sizeof(CTask*));
	}
	~CQueue()
	{
		m_nBegin = -1;					// 头尾
		m_nEnd = 0;
		m_nSize = 0;
		delete[] m_pArrTask;
		m_pArrTask = 0;
	}

	// 添加元素
	bool my_push(CTask* pTask)
	{
		// 判断队列是不是满了
		if(pTask == 0 || m_nSize == SIZE_BUFFER-1)
		{
			return false;
		}

		// 添加元素
		m_pArrTask[m_nEnd] = pTask;
		m_nSize++;
		m_nEnd++;
		
		// 判断有没有超出范围
		if(m_nEnd >= SIZE_BUFFER)
			m_nEnd = 0;

		// 判断是不是第一个元素,是的话需要将begin++
		if(m_nBegin == -1)
			m_nBegin++;

		return true;
	}

	// 删除元素
	void my_pop()
	{
		// 若队列中没有东西,则重置begin和end
		if(m_nSize == 0)
		{
			m_nBegin = -1;
			m_nEnd = 0;
			return;
		}
		// 在头部删除
		m_pArrTask[m_nBegin] = 0;
		m_nBegin++;
		m_nSize--;

		// 判断有没有超出范围
		if(m_nBegin == SIZE_BUFFER)
			m_nBegin = 0;
	}

	// 判断是否为空
	bool my_isempty()
	{
		if(m_nSize == 0)
			return true;
		return false;
	}

	// 取头元素
	CTask* my_front()
	{
		return m_pArrTask[m_nBegin];
	}
};

1.2 进行调用

1.将 CMyThreadPool 类中的任务队列成员变量更改为 CQueue m_quTask ;
2.在投递任务函数 CMyThreadPool::PushTask 中将压任务入任务队列的相关操作更改为新写的 CQueue 类的函数;
3.将线程处理函数 CMyThreadPool::ThreadProc 中有关任务队列的操作也更改为新写的 CQueue 类的函数;

// 投递任务
bool CMyThreadPool::PushTask(CTask* p_task)
{
	// 先判断给进来的任务是不是空
	if(p_task == NULL)
		return false;

	// 把任务放到任务队列中
	m_lockList.MyLock();
	// 需要加一个判断来让用户知道是否将任务压入了队列
	if(m_quTask.my_push(p_task) == false)
	{
		m_lockList.MyUnlock();
		return false;
	}
	

	// 看看有没有空闲的线程
	if(m_dwRunThreadCount < m_dwCurrentThreadCount)
	{
		// 有空闲的线程则给其释放一个信号量
		::ReleaseSemaphore(m_hSemaphoreRun, 1, 0);
	}
	else if(m_dwCurrentThreadCount < m_dwMaxThreadCount)
	{
		// 没有空闲的线程,但是可以继续创建
		HANDLE h_thread = (HANDLE)_beginthreadex(0, 0, &CMyThreadPool::ThreadProc, this, 0, 0);
		if(h_thread != 0)
		{
			m_lsThread.push_back(h_thread);
			m_dwCurrentThreadCount++;
			// 释放信号让其开始执行
			::ReleaseSemaphore(m_hSemaphoreRun, 1, 0);
		}
	}
	else
	{
		// 没有其他情况了
	}
	m_lockList.MyUnlock();

	return true;
}

// 线程处理函数
unsigned int _stdcall CMyThreadPool::ThreadProc( void * pVoid)
{
	CMyThreadPool* p_this = (CMyThreadPool*)pVoid;
	while (p_this->m_bQuitThread)
	{
		if(::WaitForSingleObject(p_this->m_hSemaphoreRun, 10) == WAIT_TIMEOUT)
			continue;

		// 每运行一个线程就自加一次
		// ::InterlockedIncrement(&(p_this->m_dwRunThreadCount));
		p_this->m_lockList.MyLock();
		p_this->m_dwRunThreadCount++;
		p_this->m_lockList.MyUnlock();

		// 不停得在任务队列中取任务出来执行
		while (1)
		{
			CTask* p_task;
			p_this->m_lockList.MyLock();
			if(p_this->m_quTask.my_isempty() == true)
			{
				p_this->m_lockList.MyUnlock();
				break;
			}
			p_task = p_this->m_quTask.my_front();
			p_this->m_quTask.my_pop();
			p_this->m_lockList.MyUnlock();

			p_task->RunTask();
			delete p_task;
			p_task = 0;
			::Sleep(0);						// 降低CPU的占有率
		}

		// 每退出一个线程就自减一次
		// ::InterlockedDecrement(&(p_this->m_dwRunThreadCount));
		p_this->m_lockList.MyLock();
		p_this->m_dwRunThreadCount--;
		p_this->m_lockList.MyUnlock();
	}
	return 0;
}

2.使用线程池来完成之前 Clean 项目的搜索文件任务

2.1 给 TabCtrl 控件上的第四个按钮 扫描删除 中添加一个新的对话框

1.新建一个对话框,在其上添加两个按钮 创建线程池 以及 搜索 按钮;
2.更改对话框的 Border 属性修改为 None (无边框), Style 属性修改为 Child (内建于主窗口中);
3.给新对话框添加类 class CDialogThreadPool : public CDialogEx ;
4.在 CMyTabCtrl 类中添加新添加的对话框的对象: CDialogThreadPool m_dlgTP ;
5.在 CMyTabCtrl::InitTabCtrl 函数中对 m_dlgTP 进行显示: m_dlgTP.Create(IDD_THREADPOOL,this) ; m_dlgTP.ShowWindow(SW_HIDE) ; m_dlgTP.MoveWindow(&rect) ;
6.在点击 Tab 列表时选择控制显示的对话框;

// CDialogThreadPool 对话框

class CDialogThreadPool : public CDialogEx
{
	DECLARE_DYNAMIC(CDialogThreadPool)

public:
	CDialogThreadPool(CWnd* pParent = NULL);   // 标准构造函数
	virtual ~CDialogThreadPool();

// 对话框数据
	enum { IDD = IDD_THREADPOOL };

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnBnClickedButton1();
	afx_msg void OnBnClickedButton2();
};

// CMyTabCtrl 初始化函数
void CMyTabCtrl::InitTabCtrl()
{
	... ...

	// 线程池对话框
	m_dlgTP.Create(IDD_THREADPOOL,this);
	m_dlgTP.ShowWindow(SW_HIDE);

	... ...
	m_dlgTP.MoveWindow(&rect);
}

2.2 完成创建线程池按钮

1.将线程池项目中的 MyThreadPool.h 文件与 MyThreadPool.cpp 文件拷贝到Clean项目下,并将这两个文件添加到Clean项目中;
2.在类 CDialogThreadPool 中定义一个线程池对象 CMyThreadPool m_thread_pool ;
3.在按钮 创建线程池 的处理函数中创建线程池: m_thread_pool.CreateThreadPool() ;

void CDialogThreadPool::OnBnClickedButton1()
{
	// 创建线程池
	if(m_thread_pool.CreateThreadPool() == true)
		MessageBox(_T("创建成功"), _T("提示"), MB_OK);
	else
		MessageBox(_T("创建失败"), _T("提示"), MB_OK);
}

2.3 完成 搜索 按钮

1.在搜索按钮中,我们首先确定哪些任务需要多任务来完成:基本就是在主窗口中搜索按钮中,那些需要从列表中取下来的路径,然后将队列更改为向线程池中push任务可以用线程池来完成;
2.创建一个 CTask 的子类: class CSearchTask : public CTask ,用来作为本次的搜索任务,其中有一个 CString m_strPath 成员,用来作为搜索的初始路径,在初始化函数 InitTask 被赋值;
3.首先获取 CCleanDlg 句柄,因为需要其中的控件对象: CCleanDlg* p_cleanDlg = (CCleanDlg*)theApp.m_pMainWnd ;
4.将标签中CShowDlg里的列表项的文本取下来,判断该项是否被选中,若选中则创建一个 CSearchTask 指针用来作为要存入线程池中的任务指针;

// 定义一个任务子类
class CSearchTask : public CTask
{
private:
	CString m_strPath;
public:
	void InitTask(CString strPath)
	{
		m_strPath = strPath;
	}
	virtual void RunTask();
};

void CDialogThreadPool::OnBnClickedButton2()
{
	// 在搜索按钮中,我们首先确定哪些任务需要多任务来完成
	// 首先就是在主窗口中搜索按钮中,那些需要从列表中取下来的路径,然后将队列更改为向线程池中push任务
	//  取下来 标签中CShowDlg里的列表项的文本
	CCleanDlg* p_cleanDlg = (CCleanDlg*)theApp.m_pMainWnd;
	for(int i=0;i<p_cleanDlg->m_tab.m_dlgShow.m_lcShowPath.GetItemCount();i++)
	{
		//  判断是不是选中的
		if(p_cleanDlg->m_tab.m_dlgShow.m_lcShowPath.GetCheck(i) == TRUE)
		{
			//  取得文本
			CString strPath = p_cleanDlg->m_tab.m_dlgShow.m_lcShowPath.GetItemText(i,0);
			//  将选中的路径添加到搜索任务中
			CSearchTask* pTask = new CSearchTask;
			pTask->InitTask(strPath);
			if(m_thread_pool.PushTask(pTask) == false)
				delete pTask;
		}
	}
}

2.4 完成类 CSearchTask 中的虚函数

1.该函数中完成的即之前写过的在 CCleanDlg::OnBnClickedButton1 中完成过的功能:给定 strPath 进行搜索相关的后缀名,若匹配则存储起来;
2.由于下列大量代码需要用到 CCleanDlg 中的对象,因此先定义一个 CCleanDlg 指针;
3.在 m_strPath 路径下开始查找具有相同后缀名的文件;
4.若获得的是文件夹,则将该文件夹继续作为一个任务压入线程池中;若是文件则与之前代码大体相同;

void CSearchTask::RunTask()
{
	// 由于下列代码是从之前写的 搜索 按钮中拷贝下来的,因此部分代码需要用到主窗口句柄
	CCleanDlg* p_cleanDlg = (CCleanDlg*)theApp.m_pMainWnd;
	// 以下代码为拷贝的 CCleanDlg::OnBnClickedButton1 中的代码
	//  在这个路径下查找文件
	CFileFind file;
	BOOL bflag = file.FindFile(m_strPath+"\\*.*");
	while(bflag)
	{
		bflag = file.FindNextFile();

		// 过滤 . 和 ..  文件
		if(file.IsDots() == TRUE)
			continue;

		//  获取名字
		CString strFileName = file.GetFileName();
		//  判断 是文件夹,则继续将当前文件夹的路径作为参数创建一个任务,并将该任务压入线程池中
		if(file.IsDirectory() == TRUE)
		{
			//  和 从队列取出来的文件夹的路径拼接  放回到队列
			CSearchTask* pTask = new CSearchTask;
			pTask->InitTask(m_strPath+'\\'+strFileName);
			if(p_cleanDlg->m_tab.m_dlgTP.m_thread_pool.PushTask(pTask) == false)
				delete pTask;
		}
		else   //  是文件
		{
			//  截取后缀名
			int index = strFileName.ReverseFind('.');
			if(index != -1)
			{
				CString strExName = strFileName.Right(strFileName.GetLength()-index);
				//  和CSetDlg 里装后缀名的集合匹配
				if(p_cleanDlg->m_tab.m_dlgSet.m_stExName.count(strExName) == 1)
				{
					//  匹配成功 把这个文件的路径 插入到m_lcDeletPath 列表
					p_cleanDlg->m_lcDeletPath.InsertItem(
						p_cleanDlg->m_lcDeletPath.GetItemCount(),
						m_strPath+'\\'+strFileName);
				}
			}
		}
	}
}

3.编程过程中出现的错误

3.1 出现如下错误

error C2146:语法错误,缺少“;”,(在标识符 “m_tab” 的前面)
error C4430:缺少类型说明符-假定为int。注意:C++不支持默认int

1.出现上述的原因我以为是缺少 “;” 导致的,但是在我检查过之后发现并不是,因为我创建 DialogThreadPool 类的时候,可以将这个新建的对话框显示出来,当我完成整个程序时就出现了上述错误,所以不是缺少 “;” 的问题;再检查了检查头文件,发现我在 DialogThreadPool 类的头文件以及源文件中都包含了头文件 CCleanDlg.h ,那么说明上述错误是重复引用所导致的,将其中一个删除之后,程序正常运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值