描述
用循環隊列存放需要打印的作業任務,並用多線程的方式分別對臨界區進行添加作業和打印作業。
具體思路
- 創建臨界區對象和兩個子線程。一個子線程調用的作爲參數的函數用於向隊列中添加作業;另一個子線程則取出隊首任務並打印。
- 當隊列滿時,向隊列中添加作業的子線程會陷入阻塞,等待打印線程的執行;而當隊列空時,打印線程就會陷入阻塞,等待向隊列添加作業的線程執行。
- 在退出程序前需要進行掃尾工作(打印完剩下的作業)。
- 在整個程序執行過程中,主進程需要保持阻塞。否則主進程結束,子線程即使未執行完也會結束。
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 (CirQueue &Q) {
Q.base = new QElemType[MAXSIZE];
if(!Q.base)
exit (1);
Q.front = Q.rear = 0;
Q.tag = 0;
Q.hw = 0;
return 0;
}
bool EnQueue(CirQueue &Q, QElemType &hw) {
if(Q.isFull()) {
printf("Queue is full now...\n");
system("pause");
return true;
}
Q.base[Q.rear] = hw;
Q.rear = (Q.rear + 1) % MAXSIZE;
Q.tag = 1;
return false;
}
bool DeQueue(CirQueue &Q, QElemType &hw) {
if(Q.isEmpty()) {
printf("Queue is empty now...\n");
system("pause");
return true;
}
hw = Q.base[Q.front];
Q.front = (Q.front + 1) % MAXSIZE;
Q.tag = 0;
return false;
}
DWORD CALLBACK addHomework(LPVOID param)
{
CirQueue *homeworks = (CirQueue*)param;
while(my_exit) {
EnterCriticalSection(&my_printer); //進入打印隊列
{
printf("Please enter the number of homeworks you want...(end by input -1)\n");
int num;
scanf("%d", &num);
if(num == -1) {
LeaveCriticalSection(&my_printer);
while(!homeworks->isEmpty()) //阻塞當前線程並清空隊列
Sleep(0);
my_exit = 0;
break;
}
for(int i = 0; i < num; i++) {
if(EnQueue(*homeworks, homeworks->hw)) {
LeaveCriticalSection(&my_printer);
while(homeworks->isFull())
Sleep(0);
EnterCriticalSection(&my_printer);
EnQueue(*homeworks, homeworks->hw);
}
printf("Homework %d entered print queue...\n", homeworks->hw);
homeworks->hw++;
}
}
LeaveCriticalSection(&my_printer);
}
return 0;
}
DWORD CALLBACK printHomework(LPVOID param)
{
CirQueue *homeworks = (CirQueue*)param;
while(my_exit) {
EnterCriticalSection(&my_printer); //進入打印隊列
{
while(!homeworks->isEmpty()) {
QElemType hw;
DeQueue(*homeworks, hw);
printf("Homework %d was printed...\n", hw);
}
}
LeaveCriticalSection(&my_printer);
while(homeworks->isEmpty())
Sleep(0);
}
return 0;
}
int main(int argc, char const *argv[])
{
InitializeCriticalSection(&my_printer);
DWORD threadId_1;
DWORD threadId_2;
HANDLE hThread_1;
HANDLE hThread_2;
CirQueue homeworks;
InitQueue(homeworks);
my_exit = 1;
hThread_1 = CreateThread(NULL, 0, addHomework, &homeworks, 0, &threadId_1);
if(hThread_1 == NULL) {
printf("Thread_1 create fail!\n");
exit(1);
}
hThread_2 = CreateThread(NULL, 0, printHomework, &homeworks, 0, &threadId_2);
if(hThread_2 == NULL) {
printf("Thread_2 create fail!\n");
exit(1);
}
while(my_exit)
Sleep(0);
system("pause");
DeleteCriticalSection(&my_printer);
return 0;
}
測試結果
設定隊列MAXSIZE = 3,打印8個作業。結果如下圖所示:
最後
- 由於博主水平有限,難免有疏漏之處,歡迎讀者批評指正!