这也是我的操作系统的实验,做完了后就发到这里。希望高手给拍拍砖,指点下错误。
实验的目的是采用多线程的方式模拟四个线程出现死锁的情况,并能进行死锁检测,要求能实现显示状态。具体模拟的是《操作系统概念》的一个题中的死锁情形(第六版,习题8.4(简单的描述下就是四个车流对四个很窄的十字路口的使用,形成环状而引起的死锁))。原题中是一个图,不好描述,嘿嘿~
上代码吧。有点多,说句实话,我觉得这次写得很差,太多太累赘了,没设计好的缘故吧,不过还是认真想了很久的。虽然不好,还是按照惯例发上来吧,毕竟以前的都发了,也希望高手指点了。总共有三个文件:
1.模拟的CPP文件
/* by: Peter_ZHA at: Oct 30th, 2009 根据第七章课后习题第一题(第六版为第八章第四题),采用多线程的方式 模拟四个线程出现死锁的情况,并能进行死锁检测,要求能实现显示线程状 态(运行态,挂起,等待,睡眠,结束)。 因为每个十字路口只能由一条车流占有,所以本题的资源可以用Windows 中的互斥量(Mutex)来模拟。 */ #include #include #include "死锁模拟和检测.h" //==========================全局数据==================================== #define RUNTIMES 5 ResAllocStat stResAllocStat = { RESNUM, PROCESSNUM, {1, 1, 1, 1}, {{0}, {0}, {0}, {0}}, {{0}, {0}, {0}, {0}} }; //工作线程,用于模拟 HANDLE rghThread[PROCESSNUM]; //记录工作线程的状态 ThreadStat rgeThreadStat[PROCESSNUM]; //互斥量,模拟资源 HANDLE rghMutex[RESNUM]; //临界区对象结构 CRITICAL_SECTION stCriticalSection; //==========================函数声明=================================== DWORD WINAPI threadFunc(LPVOID lParam); void changeAndPrintStat(ResStatChange stRSC, int nThreadID); void printResStat(ResAllocStat *pstRAS); //==========================主函数====================================== int main() { //初始化临界区结构 InitializeCriticalSection(&stCriticalSection); //创建互斥量,作为资源 for(int i = 0; i < RESNUM; i++) rghMutex[i] = CreateMutex(NULL, FALSE, NULL); for(int i = 0; i < PROCESSNUM; i++) rghThread[i] = CreateThread(NULL, 0, threadFunc, (LPVOID)i, NULL, NULL); //等待直到四个工作线程结束 WaitForMultipleObjects(PROCESSNUM, rghThread, TRUE, INFINITE); //关闭资源 for (int i = 0; i < 4; i++) { CloseHandle(rghThread[i]); CloseHandle(rghMutex[i]); } return 0; } //==========================工作线程函数================================== DWORD WINAPI threadFunc(LPVOID lParam) { int nThreadID = (int)lParam; //线程刚开始运行,状态为Run rgeThreadStat[nThreadID] = Run; /* nCarLength模拟题中车流的长度,即对资源(十字路口)占用时间的。 相对长度; nDistance模拟题中的两个相连十字路口的距离,假设等长。 注:若nCarLength > nDistance太多,会导致线程对两个资源同时占用 的时间过长,这样极易死锁;但若存在nCarLength < nDistance则不会死锁, 因为此时在请求下一资源时已释放占有的资源,不满足死锁的要求——占有 并等待。 */ int nCarLength = nThreadID * 1000000 + 50000000; //5千万 + nThread * 1百万 int nDistance = 50000000; //3千万 int nResNum1 = nThreadID; int nResNum2 = (nThreadID + 1) % RESNUM; ResStatChange stRSC; stRSC.nProcessNum = nThreadID; //在某一线程请求资源前,每个线程都已被创建 Sleep(100); for(int i = 0; i < RUNTIMES; i++) { //申请资源,提交申请信息,且更改线程状态 stRSC.eChangObject = Request; stRSC.bIntention = true; stRSC.nResNum = nResNum1; changeAndPrintStat(stRSC, nThreadID); WaitForSingleObject(rghMutex[nResNum1], INFINITE); //此时已获取申请的资源,提交分配信息,且更改线程状态 rgeThreadStat[nThreadID] = Run; stRSC.eChangObject = Allocation; changeAndPrintStat(stRSC, nThreadID); //车流经过 for(int j = 0; j < nDistance; j++); //申请下一资源,提交申请信息,且更改线程状态 stRSC.eChangObject = Request; stRSC.nResNum = nResNum2; changeAndPrintStat(stRSC, nThreadID); WaitForSingleObject(rghMutex[nResNum2], INFINITE); //此时已获取下一资源,提交分配信息,且更改线程状态 rgeThreadStat[nThreadID] = Run; stRSC.eChangObject = Allocation; changeAndPrintStat(stRSC, nThreadID); //车流流过第一个资源(十字路口) for(int j = 0; j < (nCarLength - nDistance); j++); //释放第一个资源,提交请求减少信息 stRSC.eChangObject = Request; stRSC.bIntention = false; stRSC.nResNum = nResNum1; changeAndPrintStat(stRSC, nThreadID); ReleaseMutex(rghMutex[nResNum1]); //此时已释放第一个资源,提交分配减少信息 stRSC.eChangObject = Allocation; changeAndPrintStat(stRSC, nThreadID); //车流流过第二个资源(十字路口) for(int j = 0; j < nDistance; j++); //释放第二个资源,提交请求减少信息 stRSC.eChangObject = Request; stRSC.nResNum = nResNum2; changeAndPrintStat(stRSC, nThreadID); ReleaseMutex(rghMutex[nResNum2]); //此时已释放第二个资源,提交分配减少信息 stRSC.eChangObject = Allocation; changeAndPrintStat(stRSC, nThreadID); } rgeThreadStat[nThreadID] = Over; return 0; } //==========================改变资源分配信息的函数========================= /*此函数有必要在临界区中执行,以保持线程状态的正确性,同时避免几个线程 中的此函数执行时,将所有线程挂起,而引起的死锁,这个死锁不是实验的目的. Pre:stRSC为所要进行的改变操作,nThreadID为操作针对的线程*/ void changeAndPrintStat(ResStatChange stRSC, int nThreadID) { //进入临界区 EnterCriticalSection(&stCriticalSection); //为保持监听状态的正确性,运行时将其他工作线程挂起 for(int i = 0; i < PROCESSNUM; i++) if(i != nThreadID) SuspendThread(rghThread[i]); //如果线程申请的资源可用,则改变线程状态为Run if(stRSC.eChangObject == Request && stRSC.bIntention) { if(stResAllocStat.rgnAvalible[stRSC.nResNum] > 0) rgeThreadStat[nThreadID] = Run; else rgeThreadStat[nThreadID] = Wait; } //输出此时线程的状态 for(int i = 0; i < PROCESSNUM; i++) printf("Thread %d is %s./n", i, rgszThreadStatBuffer[rgeThreadStat[i]]); //改变资源分配状态 changResStat(&stResAllocStat, stRSC); //打印资源分配状态 printResStat(&stResAllocStat); printf("/n"); //死锁检测 if(isDeadlock(&stResAllocStat)) { printf("Now deadlock occur,game over./n"); for(int i = 0; i < PROCESSNUM; i++) if( i != nThreadID) TerminateThread(rghThread[i], NULL); TerminateThread(rghThread[nThreadID], NULL); } //唤醒其他工作线程,继续模拟 for(int i = 0; i < PROCESSNUM; i++) if(i != nThreadID) ResumeThread(rghThread[i]); //退出临界区 LeaveCriticalSection(&stCriticalSection); } void printResStat(ResAllocStat *pstRAS) { printf("Allocation Request Available/n"); for(int i = 0; i < pstRAS->nProcessNum; i++) { for(int j = 0; j < pstRAS->nResNum; j++) printf("%d ", pstRAS->rgnAlloction[i][j]); printf(" "); for(int j = 0; j < pstRAS->nResNum; j++) printf("%d ", pstRAS->rgnRequest[i][j]); if(i == 0) { printf(" "); for(int j = 0; j < pstRAS->nResNum; j++) printf("%d ", pstRAS->rgnAvalible[j]); } printf("/n"); } }
2.函数的定义.h文件:
/* by: Peter_ZHA at: Oct 30th, 2009 定义了记录资源分配状态和资源分配改变信息的数据结构,和对资源分配 状态改变的函数,以及死锁函数 定义了线程的状态类型 */ #pragma once #include #include "rgnmanip.h" #define RESNUM 4 #define PROCESSNUM 4 //==============================类型定义================================== //-----------------记录资源分配状态------------------------- struct ResAllocStat { int nResNum; //资源类型数 int nProcessNum; //进程数量 //表示各种资源的可用实例 int rgnAvalible[RESNUM]; //表示当前各进程的资源分配情况 int rgnAlloction[PROCESSNUM][RESNUM]; //表示当前各进程的资源请求情况 int rgnRequest[PROCESSNUM][RESNUM]; }; //---------------记录资源分配更改信息----------------------- enum ChangeObject {Allocation, Request}; struct ResStatChange { //指出对信息的改变是针对分配信息还是请求信息 ChangeObject eChangObject; /* bIntention 当eChangeObject为Allocation时:true为给进程分配资源,false释放 进程指定资源 当eChangeObject为Request时:true为进程请求资源,false为进程取消 对指定资源的请求 */ bool bIntention; int nProcessNum; int nResNum; }; //---------------线程状态----------------------------------- enum ThreadStat{Run, Wait, Over}; char* rgszThreadStatBuffer[3] = {"Run", "Wait", "Over"}; //===============================函数声明================================ //改变资源分配和请求状态,由stRSC.eChangeObject指定改变信息的对象 void changResStat(ResAllocStat* pstRAS, ResStatChange stRSC); //死锁检测 bool isDeadlock(ResAllocStat* pstRAS); //=========================更改资源分配状态的函数实现=================== void changResStat(ResAllocStat* pstRAS, ResStatChange stRSC) { //针对对分配信息的改变------------------------------------------ if(stRSC.eChangObject == Allocation) { //分配资源,Available[资源]--,Allocation[进程][资源]++,Request[进程][资源]--; if(stRSC.bIntention) { pstRAS->rgnAvalible[stRSC.nResNum]--; pstRAS->rgnAlloction[stRSC.nProcessNum][stRSC.nResNum]++; pstRAS->rgnRequest[stRSC.nProcessNum][stRSC.nResNum]--; } //释放资源,Available[资源]++,Allocation[进程][线程]++; else { pstRAS->rgnAvalible[stRSC.nResNum]++; pstRAS->rgnAlloction[stRSC.nProcessNum][stRSC.nResNum]--; } } //针对对请求信息的改变----------------------------------------- else { //增加资源请求 if(stRSC.bIntention) pstRAS->rgnRequest[stRSC.nProcessNum][stRSC.nResNum]++; //减少资源请求 else pstRAS->rgnRequest[stRSC.nProcessNum][stRSC.nResNum]--; } } //===============由此时的资源分配状态判断是否形成死锁================== //算法参考操作系统概念(中文第六版),8.6.2,P192 bool isDeadlock(ResAllocStat* pstRAS) { bool bIsDeadlock = false; //-------------第一步------------------------------------- int *rgnWork = (int*)malloc(sizeof(int) * pstRAS->nResNum); bool *rgbFinish = (bool*)malloc(sizeof(bool) * pstRAS->nProcessNum); doRgCopy(rgnWork, pstRAS->rgnAvalible, pstRAS->nResNum); for(int i = 0; i < pstRAS->nProcessNum; i++) { rgbFinish[i] = isAllEqual(pstRAS->rgnAlloction[i], pstRAS->nResNum, 0); } while(true) { //----------第二步-------------------------------------- int nIndex = -1; for(int i = 0; i < pstRAS->nProcessNum; i++) { if(rgbFinish[i] == false && doRgOperate(pstRAS->rgnRequest[i], rgnWork, pstRAS->nResNum, lessAndEqual) ) { nIndex = i; break; } } if(nIndex == -1) break; //-------------------第三步-------------------------- doRgOperate(rgnWork, pstRAS->rgnAlloction[nIndex], pstRAS->nResNum, plus); rgbFinish[nIndex] = true; } //-----------------------第四步-------------------------- for(int i = 0; i < pstRAS->nProcessNum; i++) { if(!rgbFinish[i]) { bIsDeadlock = true; break; } } free(rgnWork); free(rgbFinish); return bIsDeadlock; }
3.一些数组操作的定义.h文件,这个文件的存在也是我觉得这次写得不好的原因之一,因为貌似我定义的那些函数,在库里都有的,唉……
/* by: Peter_ZHA at: Oct 30th, 2009 定义了一些针对int型数组的操作函数,方便使用 */ #pragma once //=============数组操作函数声明============================= bool doRgOperate(int *rgLeft, int *rgRight, int nLen, bool (*operate)(int*, int*)); //作为doRgOperate的参数,按需求添加 bool lessAndEqual(int* pLeft, int* pRight); bool equal(int* pLeft, int* pRight); bool minus(int* pLeft, int* pRight); bool plus(int* pLeft, int* pRight); //数组复制 void doRgCopy(int *rgDest, int* rgRes, int nLen); //检验数组是否全为nParam bool isAllEqual(int *rgCheck, int nLen, int nParam = 0); //==========================几个数组操作函数的实现======================= void doRgCopy(int *rgDest, int* rgRes, int nLen) { for(int i = 0; i < nLen; i++) rgDest[i] = rgRes[i]; } bool doRgOperate(int *rgLeft, int *rgRight, int nLen, bool (*operate)(int*, int*)) { for(int i = 0; i < nLen; i++) { if( operate(&rgLeft[i], &rgRight[i])) continue; return false; } return true; } bool lessAndEqual(int* pLeft, int* pRight) { return *pLeft <= *pRight; } bool equal(int* pLeft, int* pRight) { return *pLeft == *pRight; } bool minus(int* pLeft, int* pRight) { *pLeft -= *pRight; return true; } bool plus(int* pLeft, int* pRight) { *pLeft += *pRight; return true; } bool isAllEqual(int *rgCheck, int nLen, int nParam) { for(int i = 0; i < nLen; i++) if(rgCheck[i] != nParam) return false; return true; }
写得不好,希望看到了的高手指导下,谢谢了!
欢迎转载(可能性更小了),还是注明出处吧:
http://blog.csdn.net/zha_1525515/archive/2009/11/08/4787250.aspx