數據結構-利用(循環隊列)和(多線程編程)模擬實現打印作業任務管理

描述

循環隊列存放需要打印的作業任務,並用多線程的方式分別對臨界區進行添加作業和打印作業。

具體思路

  1. 創建臨界區對象和兩個子線程。一個子線程調用的作爲參數的函數用於向隊列中添加作業;另一個子線程則取出隊首任務並打印。
  2. 當隊列滿時,向隊列中添加作業的子線程會陷入阻塞,等待打印線程的執行;而當隊列空時,打印線程就會陷入阻塞,等待向隊列添加作業的線程執行。
  3. 在退出程序前需要進行掃尾工作(打印完剩下的作業)。
  4. 在整個程序執行過程中,主進程需要保持阻塞。否則主進程結束,子線程即使未執行完也會結束。

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); 離開臨界區

函數EnterCriticalSectionLeaveCriticalSection中間的代碼表示臨界區代碼,必須成對出現,否則會出現死鎖的現象。
如果已經有線程進入了臨界區,其他線程調用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個作業。結果如下圖所示:
在這裏插入圖片描述

最後

  • 由於博主水平有限,難免有疏漏之處,歡迎讀者批評指正!

  1. 引自百度百科 ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章