仅供学习参考,如有不足之处,请予补充指正。
一、知识点回顾
为了能使程序并发执行,并且可以对并发执行的程序加以描述和控制,引入了”进程“的概念。
1、PCB(进程控制块):OS配置的专门的数据结构,系统利用PCB来描述进程的基本活动和活动过程,进而控制和管理进程
2、创建进程——创建进程实体中的PCB;撤销进程——撤销进程的PCB
3、进程是程序的一次执行,是拥有资源的独立单位
4、引入进程的目的:使多个程序能够并发执行,以提高资源利用率和系统吞吐量
5、进程特征:动态性、并发性、独立性、异步性
6、进程的三种状态:就绪状态、执行状态、阻塞状态
7、三种基本状态转换:就绪——执行(进程调度)、执行——就绪(时间片完)、执行——阻塞(I/O请求)、阻塞——就绪(I/O完成)
8、进程同步机制遵循规则:空闲让进、忙则等待、有限等待、让权等待
9、信号量机制:信号量的值仅由pv操作改变,虽然使用方便但是同步操作使用不当会导致系统死锁。
10、管程:模块化、抽象数据类型、信息掩蔽
11、管程与进程不同之处:二者都定义了数据结构,但是进程定义的——私有数据结构PCB,管程定义的——公共数据结构,如消息队列等;二者存在对各自数据结构上的操作,但进程——顺序程序执行,而管程——进行同步和初始化操作;设置进程目的——实现系统的并发性,设置管程目的——解决共享资源的互斥使用问题;进程工作方式——主动,管程工作方式——被动;进程之间能并发执行,管程不能;进程具有动态性,管程——资源管理模块,供进程调用;
12、进程通信:进程之间的信息交换
13、进程通信类型:共享存储器系统、管道(pipe)通信系统、消息传递系统、客户机-服务器系统
14、消息传递实现方式:直接消息传递系统(发送原语)、信箱通信(间接通信,信箱通信原语)
15、引入线程的目的:减少程序再并发执行时的空间开销,使得OS具有更好的并发性
16、线程:独立调度和分派的基本单位
二、效果图展示
三、详解
使用框架:VS中的MFC
主要实现功能:画圆、画方、按钮并发
实现函数:
UINT DrawRectgle(LPVOID lpParam);//画圆
UINT DrawRectgle(LPVOID lpParam);//画方具体实现:
UINT DrawCircle(LPVOID lpParam) { CWinThreadDlg* pDlg = (CWinThreadDlg*)lpParam; CDC* pDC; //GetDC()为一个指定窗口的客户端区域或者整个屏幕从一个设备上下文(DC)中提取一个句柄 pDC = pDlg->GetDC(); CPoint CirclePoint(180, 180);//原点 COLORREF crColour = RGB(0, 0, 255);//颜色 int R0 = 150; //半径 const double degree_half = 0.008726; //每半度 2×3.1415 / 720 int i = 0; for (i = 0; i < 720; i++) { //SetPixelV()在指定的设备场景中设置一个像素的RGB值 pDC->SetPixelV( CirclePoint.x + R0 * sin(degree_half * i), CirclePoint.y + R0 * cos(degree_half * i), crColour); Sleep(10); } return 0; }
UINT DrawRectgle(LPVOID lpParam) { CWinThreadDlg* pDlg = (CWinThreadDlg*)lpParam; CDC* pDC; pDC = pDlg->GetDC(); CPoint CirclePoint(180, 180);//原点 COLORREF crColour = RGB(255, 0, 0);//颜色 int R0 = 150; //半径 const double degree_half = 0.008726; //每半度 2×3.1415 / 720 //换个位置画方 CirclePoint.x = CirclePoint.x + 2 * R0 + 10; float factor = 1.65; //屏幕宽度长度不协调,需要调节!!! float Delta = 2 * R0 / 180 * factor; int ii = 0; //上边 for (ii = 0; ii < 180; ii++) { pDC->SetPixelV( CirclePoint.x + R0 - Delta * ii, CirclePoint.y - R0, crColour); Sleep(10); } //左边 for (ii = 0; ii < 180; ii++) { pDC->SetPixelV( CirclePoint.x - R0, CirclePoint.y - R0 + Delta * ii, crColour); Sleep(10); } //下边 for (ii = 0; ii < 180; ii++) { pDC->SetPixelV( CirclePoint.x - R0 + Delta * ii, CirclePoint.y + R0, crColour); Sleep(10); } //右边 for (ii = 0; ii < 180; ii++) { pDC->SetPixelV( CirclePoint.x + R0, CirclePoint.y + R0 - Delta * ii, crColour); Sleep(10); } return 0; }
按钮并行画圆画方实现:
MFC提供了两个重载版的AfxBeginThread,一个用于用户界面线程,另一个用于工作者线程,区别在于用户界面线程能处理消息响应,而工作者线程不能。
用户线程原型:
CWinThread* AFXAPI AfxBeginThread ( CRuntimeClass* pThreadClass,//派生类 int nPriority, //优先级 UINT nStackSize, //堆栈大小 DWORD dwCreateFlags,//创建标识 LPSECURITY_ATTRIBUTES lpSecurityAttrs//安全属性,NT下有用 )
举例:
工作者线程原型:
CWinThread* AFXAPI AfxBeginThread ( //线程的入口函数,声明一定要如下: UINT MyThreadFunction( LPVOID pParam ); AFX_THREADPROC pfnThreadProc, LPVOID pParam,//传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体 int nPriority, //优先级 UINT nStackSize, //堆栈大小 DWORD dwCreateFlags,//创建标识 LPSECURITY_ATTRIBUTES lpSecurityAttrs //安全属性 )
AfxBeginThread除前面两个参数外,后面几个都是默认参数,可以省略。
在这里我们用的是工作者线程,第一个参数是线程入口函数,第二个参数把this传过去,线程函数可以使用和操作类的成员。
线程函数是静态类函数成员
// 画圆和画方(线程方式) //同时调用两个函数分别画圆和画方 void CWinThreadDlg::OnBUTTONThread() { // 画圆的函数:DrawCircle pThread_Circle = AfxBeginThread(DrawCircle, this); // 画方的函数 pThread_Rectgle = AfxBeginThread(DrawRectgle, this); }
终止线程进程:碰到 return 0; 就会调用释放函数结束线程,这是释放线程最推荐的方式。
其余方式:
ExitProcess、 ExitThread函数强迫线程终止运行,会导致OS清理该线程使用的所有操作系统资源,但是C/C++资源不会被销毁,要避免使用, 不要自己调用;
TerminateProcess、 TerminateThread函数是异步的,并不能保证线程终止,要避免使用;