⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 16.3 线程死锁16.txt

📁 网上第一本以TXT格式的VC++深入详解孙鑫的书.全文全以TXT格式,并每一章节都分了目录,清晰易读
💻 TXT
字号:
16.3 线程死锁
有一个哲学家进餐的问题能够很好地描述线程死锁。哲学家进餐的问题是这样的:有t 多位哲学家一
起用餐,但每人只有一支筷子,当然用一支筷子是无法吃食物的。这时,如果有一位哲学家能将他
的那只筷子交出来,让其他哲学家先吃,之后,再将一双筷子交回来,这样,所有哲学家就都能够
吃到食物了。但是哲学家们考虑问题时想得比较深,他们担心把筷子交给别人先吃,那么别人吃完
食物之后,不把筷子交回来的话,自己不就吃不到食物了吗?所以他们都希望其他人先把筷子交出来,
让自己先吃。然而由于每位哲学家都这样想,从而导致每位哲学家都不肯交出筷子,于是所有的哲
学家看着满桌的美食,可就是吃不到。这就是一个线程死锁的实例。
对多线程为说,如果线程1拥有了临界区对象A,等待临界区对象B的拥有权,线程 2拥有了临界区对
象B,等待临界区对象A的拥有权,这就造成了死锁。下面通过代码来演示线程死锁的发生。我们在
已有的Critical程序上进行修改,结果如例 16-5所示。 
例J 16-5 
#include <Windows . h> 
#inc	lude <iostream.h> 
DWORD WINAPI Fun1Proc( LPVOID lpParameter 11 thread data 
DWORD WINAPI Fun2Proc( 
LPVOID lpParameter 11 thread data 
int tickets=100; 
CRITICAL_SECTION g_csA; 
CRITICAL_SECTION g_csB; 
void main() 
HANDLE hThread1;
HANDLE hThread2; 
hThread1=CreateThread(NULL , 0, Fun1Proc , NULL , 0, NULL); 
hThread2=CreateThread(NULL, 0 , Fun2Proc , NULL , 0 , NULL); 
CloseHandle(hThread1); 
CloseHandle(hThread2); 

InitializeCriticalSection(&g_csA); 
InitializeCriticalSection(&g_csB); ; 
Sleep(4000); 
 
DeleteCriticalSection(&g_csA); 

DeleteCriticalSection(&g_csB); 


DWORD WINAPI Fun1Proc( LPVOID lpParameter // thread data 
while (TRUE) 
EnterCriticalSection(&g_csA); 
Sleep(1); 
EnterCriticalSection(&g_csB); 
if(tickets>0) 

Sleep(1); 
cout<<"threadl sell ticket : "<<tickets--<<endl; 
LeaveCriticalSection(&g_csB); 
LeaveCriticalSection(&g_csA); 

} 
else 
{
LeaveCriticalSection(&csa);
LeaveCriticalSection(&csb);
break;
}
}
cout<<"fun1proc is running"<<endl;
return 0; 

DWORD WINAPI Fun2Proc( LPVOID lpPararneter // thread data 
while (TRUE) 
EnterCriticalSection(&g_csB); 
Sleep (1) ; 
EnterCriticalSection(&g_csA); 
if(tickets>0) 

{ 
Sleep (1) ; 
cout<<"thread2 sell ticket : "<<tickets--<<endl; 
LeaveCriticalSection(&g_csA) ; 
LeaveCriticalSection(&g_csB) ; 

else 
LeaveCriticalSection(&g一cSA) ; 
LeaveCriticalSection(&g_csB); 
break; ' 

cout<<"thread2 is running!"<<endl; 
return 0; 

首先,上述例 16-5所示代码创建了两个临界区对象 :G_csA和 g_csB。在程序中,如果需要对某一种
资源进行保护的话,就可以构建一个临界区对象。这与现实生活中的情况是一样的,例如如果在程
序中想要保护电话这种资源的话,那就构建一个临界区对象来实现,就好像建立一个电话亭一样:
如果在程序中还想访问自动柜员机这种资源的话,就可以再创建一个临界区对象,对自动柜员机这
种资源进行保护。
接着,上述例 16-5所示代码在 main函数中,调用 InitializeCriticalSection函数对新创建的两
个临界区对象 G_csA和 G_csB都进行了初始化,并在程序退出前调用 DeleteCritical Section函数释
放这两个临界区对象(已经没有任何线程拥有它们了)的所有资源。
然后,在线程 1中调用 EnterCriticalSection函数先请求临界区对象: g_csA的所有权,当得到该
所有权后,再去请求临界区对象 =ιcsB的所有权。当线程 1访问完保护的资源后,调用
LeaveCriticalSection函数释放两个临界区对象的所有权。注意 z因为调用 LeaveCriticalSection
函数时,该函数会立即返回,并不会导致线程等待,所以释放临界区对象所有权的顺序是无所谓的。
对线程 2来说,官先请求临界区对象 g_csB的所有权,然后再去等待临界区对象 ιcsA的所有权。在
访问完保护的资源之后,释放所有临界区对象的所有权。 
下面,我们来分析上述程序的执行过程。当线程 1得到临界区对象 g_csA的所有权之后,调用 Sleep
函数,该线程将睡眠 lms,放弃执行机会。于是,操作系统会选择线程 2执行,该线程首先等待的
是临界区对象 g_csB的所有权,当它得到该所有权之后,调用 Sleep函数,让线程 2也睡眠 lms。
于是,轮到线程 1执行,这时它需要等待临界区对象 ιcsB的所有权。然而这时临界区对象 g_csB己
经被线程 2所拥有,因此线程 l就会等待。当线程 1等待时,线程 2开始执行,这时它需要等待临
界区对象 ιcsA的所有权。然而临界区对象 ιcsA的所有权己经被线程 1所拥有,因此线程 2也进入
等待状态。这样就导致线程 1和线程 2都在等待对方交出临界区对象的所有权,于是就造成了死锁。
我们可以运行这时的 CriticaJ程序,将会看到如图 16.4所示的结果。可以看到,线程 l和线程 2
都没有执行关键代码段中的代码,说明它们都没有得到所需的临界区对象的所有权。

图 16 .4线程死锁结果

因此在利用多线程技术编写程序的过程中,在实现线程同步时一定要多加注意,应避免发生线程死
锁。 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -