描述
用循环队列存放需要打印的作业任务,并用多线程的方式分别对临界区进行添加作业和打印作业。
具体思路
- 创建临界区对象和两个子线程。一个子线程调用的作为参数的函数用于向队列中添加作业;另一个子线程则取出队首任务并打印。
- 当队列满时,向队列中添加作业的子线程会陷入阻塞,等待打印线程的执行;而当队列空时,打印线程就会陷入阻塞,等待向队列添加作业的线程执行。
- 在退出程序前需要进行扫尾工作(打印完剩下的作业)。
- 在整个程序执行过程中,主进程需要保持阻塞。否则主进程结束,子线程即使未执行完也会结束。
CreateThread()
线程创建函数说明:
CreateThread(
_In_opt_LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_SIZE_T dwStackSize,
_In_LPTHREAD_START_ROUTINE lpStartAddress,
_In_opt_drv_aliasesMemLPVOID lpParameter,
_In_DWORD dwCreationFlags,
_Out_opt_LPDWORD lpThreadId
);
参数说明:1
- lpThreadAttributes:指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,NULL使用默认安全性,不可以被子线程继承,否则需要定义一个结构体将它的bInheritHandle成员初始化为TRUE
- dwStackSize,设置初始栈的大小,以字节为单位,如果为0,那么默认将使用与调用该函数的线程相同的栈空间大小。任何情况下,Windows根据需要动态延长堆栈的大小。
- lpStartAddress,指向线程函数的指针,形式:@函数名,函数名称没有限制。
- lpParameter:向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL。
- dwCreationFlags :线程标志,可取值如下
(1)CREATE_SUSPENDED(0x00000004):创建一个挂起的线程,
(2)0:表示创建后立即激活。
(3)STACK_SIZE_PARAM_IS_A_RESERVATION(0x00010000):dwStackSize参数指定初始的保留堆栈 的大小,否则,dwStackSize指定提交的大小。该标记值在Windows 2000/NT and Windows Me/98/95上不支持。 - lpThreadId:保存新线程的id。
返回值:函数成功,返回线程句柄;函数失败返回false。若不想返回线程ID,设置值为NULL。
临界区操作函数
下面是一些对临界区操作的主要函数:
- InitializeCriticalSection (LPCRITICAL_SECTION lpCriticalSection); 初始化临界区对象
- DeleteCriticalSection (LPCRITICAL_SECTION lpCriticalSection); 销毁临界区对象
- EnterCriticalSection (LPCRITICAL_SECTION lpCriticalSection); 进入临界区
- LeaveCriticalSection (LPCRITICAL_SECTION lpCriticalSection); 离开临界区
函数EnterCriticalSection
和LeaveCriticalSection
中间的代码表示临界区代码,必须成对出现,否则会出现死锁的现象。
如果已经有线程进入了临界区,其他线程调用EnterCriticalSection
则会被阻塞,并使线程进入休眠状态,直到进入临界区的线程离开,同时处理器调度该线程进入临界区,该线程才会被唤醒并进入临界区。
实现代码
#include<stdio.h>
#include<iostream>
#include "Windows.h"
int my_exit = 0;
CRITICAL_SECTION my_printer; //创建临界区对象
typedef int QElemType;
#define MAXSIZE 3
typedef struct {
QElemType *base;
int front;
int rear;
int tag;
bool isEmpty() {
return front == rear && tag == 0;
}
bool isFull() {
return front == rear && tag == 1;
}
QElemType hw; //作业序号
}CirQueue;
int InitQueue (</