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

1.复习

1.1 进程的组成部分

1.进程堆栈
2.内核对象
计数器
挂起计数器
信号:用来退出线程(若是出现改变flag无法退出线程时)

1.2 计数器为零时回收内核对象

1.3 进度条线程创建

1.创建一个基于对话框的MFC应用程序
2.添加一个 Progress Control 控件,添加一个变量 CProgressCtrl m_progress ;
3.添加两个按钮 开始下载 和 停止下载 ;
4.在 开始下载 按钮中创建线程: ::CreateThread(0, 0, &CThreadDlg::ThreadProc, this, 0, 0) ;
5.其中的 &CThreadDlg::ThreadProc 是线程处理函数,其是一个静态函数: static DWORD WINAPI ThreadProc(In LPVOID lpParameter) ;
6.添加一个成员变量句柄用来记录线程句柄: HANDLE m_hThread ;添加一个布尔型变量用来记录是否跑进度条: bool m_bIsQuitThread ;
8.在 开始下载 按钮处理函数中,首先判断线程句柄是否是空,为空则创建线程;
9.在静态函数 CThreadDlg::ThreadProc 中,首先获取线程句柄: CThreadDlg* pThis = (CThreadDlg*)lpParameter ;
10.设置进度条移动步长: pThis->m_progress.SetStep(4) ;
11.循环移动进度条: pThis->m_progress.StepIt() ;

void CThreadDlg::OnBnClickedButton1()
{
	if(m_hThread != 0)
		return;
	// 创建线程
	m_hThread = ::CreateThread(0, 0, &CThreadDlg::ThreadProc, this, 0, 0);
	if(m_hThread == 0)
		MessageBox(_T("创建失败"));
	else
	{
		m_bIsQuitThread = true;
	}
}


void CThreadDlg::OnBnClickedButton2()
{
	m_bIsQuitThread = false;
	if(::WaitForSingleObject(m_hThread, 300) == WAIT_TIMEOUT)
		::TerminateThread(m_hThread, -1);
	::CloseHandle(m_hThread);
	m_hThread = 0;
}

DWORD WINAPI CThreadDlg::ThreadProc(_In_  LPVOID lpParameter)
{
	CThreadDlg* pThis = (CThreadDlg*)lpParameter;
	pThis->m_progress.SetStep(4);
	while (pThis->m_bIsQuitThread)
	{
		pThis->m_progress.StepIt();
		::Sleep(200);
	}
	return 0;
}

1.4 事件退出线程

1.首先创建事件句柄变量: HANDLE m_hQuitEvent ;
2.在构造函数中创建事件: ::CreateEvent ;首先定义手动重置信号;
3.将线程处理函数中将循环置为 1 ,在循环过程中等待信号: ::WaitForSingleObject(pThis->m_hQuitEvent, 10) == WAIT_OBJECT_0 ;
4.有信号则退出;
5.在 停止下载 按钮中,设置有信号: ::SetEvent ;

CThreadDlg::CThreadDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CThreadDlg::IDD, pParent)
{
	m_hThread = 0;
	m_bIsQuitThread = true;

	m_hQuitEvent = ::CreateEvent(
		0,							// 安全属性
		TRUE,						// true 表示人工重置
		FALSE,						// false 为无信号
		0							// 事件 名
		);


	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CThreadDlg::OnBnClickedButton2()
{
	// 2.事件退出
	::SetEvent(m_hQuitEvent);
	if(::WaitForSingleObject(m_hThread, 300) == WAIT_TIMEOUT)
		::TerminateThread(m_hThread, -1);
	::CloseHandle(m_hThread);
	m_hThread = 0;
}

// 事件退出线程
DWORD WINAPI CThreadDlg::ThreadProc(_In_  LPVOID lpParameter)
{
	CThreadDlg* pThis = (CThreadDlg*)lpParameter;
	pThis->m_progress.SetStep(4);
	while (1)
	{
		if(::WaitForSingleObject(pThis->m_hQuitEvent, 10) == WAIT_OBJECT_0)
			break;
		pThis->m_progress.StepIt();
		::Sleep(200);
	}

	// 当人工重置时需要设置信号为无信号
	::ResetEvent(pThis->m_hQuitEvent);

	return 0;
}

6.设置信号句柄为自动重置;

CThreadDlg::CThreadDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CThreadDlg::IDD, pParent)
{
	m_hThread = 0;
	m_bIsQuitThread = true;
	m_hQuitEvent = ::CreateEvent(
		0,							// 安全属性
		FALSE,						// false 表示自动重置
		FALSE,						// false 为无信号
		0							// 事件 名
		);

	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CThreadDlg::OnBnClickedButton2()
{
	// 2.事件退出
	::SetEvent(m_hQuitEvent);
	if(::WaitForSingleObject(m_hThread, 300) == WAIT_TIMEOUT)
		::TerminateThread(m_hThread, -1);
	::CloseHandle(m_hThread);
	m_hThread = 0;
}

// 事件退出线程
DWORD WINAPI CThreadDlg::ThreadProc(_In_  LPVOID lpParameter)
{
	CThreadDlg* pThis = (CThreadDlg*)lpParameter;
	pThis->m_progress.SetStep(4);
	while (1)
	{
		if(::WaitForSingleObject(pThis->m_hQuitEvent, 10) == WAIT_OBJECT_0)
			break;
		pThis->m_progress.StepIt();
		::Sleep(200);
	}

	return 0;
}

1.5 通过另一个进程来停止当前线程

1.在当前 解决方案 中新建一个基于对话框的项目;
2.在新建的对话框中添加一个按钮: 停止线程 ;
3.给 停止线程 按钮添加事件处理函数,在该函数中首先打开事件: OpenEvent ;
4.给事件句柄一个信号,即可停止另一个对话框中的线程;

void CAnotherDlgDlg::OnBnClickedButton1()
{
	// 事件退出进度条
	HANDLE h_progress = ::OpenEvent(
		EVENT_ALL_ACCESS,			// 安全属性
		FALSE,						// 继承性
		L"HH"						// 事件 ID ,需要将另一个对话框中的事件 ID 由 0 改为 L"HH" 
		);
	::SetEvent(h_progress);
	::CloseHandle(h_progress);
	h_progress = 0;
}

2.线程的状态

2.1 线程共有5种状态

1.新建状态
2.就绪状态
3.运行状态
4.阻塞状态
5.死亡状态

3.线程的种类

3.1 线程一般分为两类

1.工作线程
2.UI线程: CWinThread

4.线程间通信

4.1 线程间通信的方法

1.通过在同一进程的全局变量来通信
2.通过消息来进行通信
3.通过剪贴板来进行
4.内存文件映射
5.共享库dll
6.管道(匿名管道和命名管道)
7.邮槽
8.socket套接字

4.2 完成计算对话框

1.新建一个基于对话框的MFC应用程序;
2.在对话框上添加两个按钮 创建线程 计算 ,添加三个 Radio Control 控件 1+2+3+…+10 1+2+3+…+100 1+2+3+…+1000 ,一个 Edit Control 对话框用来显示计算的结果;
3.首先给主对话框的类添加一个线程句柄: HANDLE m_hThread ;因为需要事件退出,在添加一个事件句柄: HANDLE m_hEvent ;在构造函数中创建事件 m_hEvent = ::CreateEvent(0, FALSE, FALSE, 0) (本次创建自动重置信号,初始无信号的事件);
4.在 创建线程 按钮中创建一个线程: m_hThread = ::CreateThread(0, 0, &CTESTDlg::ThreadProc, this, 0, 0) ;
5.函数 CTESTDlg::ThreadProc 是线程处理函数,是一个我们添加的静态函数: static DWORD WINAPI ThreadProc(LPVOID lpParamter) ;
6.给主对话框类添加 bool m_bQuitFlag 用来标记是否退出循环; int m_rMaxNum 用来记录点击的 Radio 控件是加到多少的;
7.在 CTESTDlg::ThreadProc 函数中,我们首先获取主对话框对象,当 m_bQuitFlag 为真时,一直计算并放到 Edit 控件上(这就需要给 Edit 控件添加一个变量),并且我们在循环时需要一个消息来进行计算(否则会一直计算并显示到 Edit 控件中,感兴趣的可以试一下不加等待消息这行语句): ::WaitForSingleObject(pThis->m_hEvent, INFINITE) (INFINITE 的意思是一直等待事件的信号);
8.在 计算 按钮中设置事件有信号: SetEvent(m_hEvent) ;
9.给 Radio 控件添加点击消息,在点击消息中更改 m_rMaxNum 的值;

void CTESTDlg::OnBnClickedButton1()
{
	if(m_hThread != 0)
		return;
	m_hThread = ::CreateThread(0, 0, &CTESTDlg::ThreadProc, this, 0, 0);
	if(m_hThread == 0)
		MessageBox(_T("创建线程失败"));
	else
		m_bQuitFlag = true;
}

DWORD WINAPI CTESTDlg::ThreadProc(LPVOID lpParamter)
{
	CTESTDlg* pThis = (CTESTDlg*)lpParamter;
	while (pThis->m_bQuitFlag)
	{
		::WaitForSingleObject(pThis->m_hEvent, INFINITE);
		int sum = 0;
		for(int i=1; i<=pThis->m_rMaxNum; i++)
			sum += i;

		CString str;
		str.Format(_T("%d"), sum);
		pThis->m_eShowSum.SetWindowText(str);
	}
	return 0;
}

void CTESTDlg::OnBnClickedButton2()
{
	::SetEvent(m_hEvent);
}

void CTESTDlg::OnBnClickedRadio1()
{
	m_rMaxNum = 10;
}

void CTESTDlg::OnBnClickedRadio2()
{
	m_rMaxNum = 100;
}

void CTESTDlg::OnBnClickedRadio3()
{
	m_rMaxNum = 1000;
}

猜你喜欢

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