数据毁坏或一个死锁几乎是一个多线程应用中发生的最坏的问题,它具有非常恶毒的和敏感的形式但相当困难重新或者被跟踪。由于这种原因,强烈推荐你在这些情况发生之前分析你的多线程应用程序可能的死锁条件并检查和删除这些死锁。
非常简单一个死锁两个或多个线程彼此等待对方释放共享资源而不释放它占有的资源。因为所有参与死锁的线程被挂起并且因此而不能释放它们占有的资源,没有线程可以继续运行,并且整个应用程序(或者更坏的,多个共享资源的应用程序)看起来被吊死了。
下面是一个很简单的多线程应用程序,我叫他GOOFY(傻瓜),方便我们后来容易引用,我们在后面经常提及它。
#include <windows.h>
#include <stdio.h>
CRITICAL_SECTION cs1,cs2;
long WINAPI ThreadFn(long);
main ()
{
long iThreadID;
InitializeCriticalSection (&cs1);
InitializeCriticalSection (&cs2);
CloseHandle(CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFn,
NULL,0,&iThreadID));
while(TRUE)
{
EnterCriticalSection(&cs1);
printf("/nThread1 has entered Critical Section 1 but not 2.");
EnterCriticalSection(&cs2);
printf("/nThread1 has entered Critical Section 1 and 2!");
LeaveCriticalSection(&cs2);
printf("/nThread1 has left Critical Section 2 but still owns 1.");
LeaveCriticalSection(&cs1);
printf("/nThread1 has left both critical sections...");
Sleep(20);
};
return(0);
}
long WINAPI ThreadFn(long lParam)
{
while(TRUE)
{EnterCriticalSection(&cs2);
printf("/nThread2 has entered Critical Section 2 but not 1.");
EnterCriticalSection(&cs1);
printf("/nThread2 has entered Critical Section 2 and 1!");
LeaveCriticalSection(&cs1);
printf("/nThread2 has left Critical Section 1 but still owns 2.");
LeaveCriticalSection(&cs2);
printf("/nThread2 has left both critical sections...");
Sleep(20);
};
}
你可以将GOOFY代码COPY到一个C源文件中,然后编译和连接它,然后运行,你会看到GOOFY运行了一段时间之后会挂起。线程1声明了关键段1并且被挂起,因为其等待关键段2,然而线程2拥有关键段2并等待线程释放关键段1,而这种情况从不会出现,当然因为线程1在线程释放关键段之前不会释放关键段1,当然,因为。。。(重复)
不幸的是,死锁并不是总是这么容易被发现。在一个复杂应用程序,几个相互依赖的条件,死锁只是在某种特定的条件下才会产生。这里有一个16位WINDOWS系统上严重死锁的小故事,它会在低级系统或第三方应用程序上出现:由于某种网络驱动器装载,WINDOWS 3.1常常进入一个状态,这时它试着打开一个虚拟的DOS对话框来提示一个空白的黑屏,你可以切换,但你它却一直保持黑屏,这被叫做“黑屏死机”;这个DOS BOX会一直在那里,知道世界末日,并且系统没有办法去掉它而重新恢复,更严重的,在有些黑屏死的情况下,整个系统就被挂起,除非你执行冷启动。
这是一个终所周知的死锁情况。当初始化DOS BOX时,WIN3.1的VMM(虚拟机管理器)发送一个请求给网卡,让它初始化,同时,基于DOS的网络驱动器参与这个黑洞查询网络卡,这是对定时器中断的周期性行为,并且它只在时间溢出或执行成功的时候返回。
不幸的是,由于WIN3.1上的VTD一些微妙的问题,可能在有些情况下,VM不会发现任何定时器中断,结果VM不会从初始化序列返回。另一方面,VMM拒绝映射更多的定时器到新创建的VM中,除非那初始化返回,那么死锁了。
任何死锁中典型的错误是他们可能能异步运行,就向前面的例子,他们在响应异步事件的时候发生,(例如,定时器,网络中断的硬件事件),就象相同的情况运行1到10次(或者20,1000或任何次)都不会有问题,但在n+1次死锁了。通常,
使情况更糟的是,有些情况他们看起来象死锁,实际上不是,他们可能仅仅在被一些资源组塞在那里需要更长时间的等待,可能是网络连接或硬件中断,这种情况下,你错误的将他们作为死锁并杀死他们,但如果你让它继续,他们可能自己恢复。
为了阻止这些尴尬的情况出现,你应该很好考虑分析你程序中存在的死锁问题。这个问题是本文的重点。
这是一个终所周知的死锁情况。当初始化DOS BOX时,WIN3.1的VMM(虚拟机管理器)发送一个请求给网卡,让它初始化,同时,基于DOS的网络驱动器参与这个黑洞查询网络卡,这是对定时器中断的周期性行为,并且它只在时间溢出或执行成功的时候返回。
不幸的是,由于WIN3.1上的VTD一些微妙的问题,可能在有些情况下,VM不会发现任何定时器中断,结果VM不会从初始化序列返回。另一方面,VMM拒绝映射更多的定时器到新创建的VM中,除非那初始化返回,那么死锁了。
任何死锁中典型的错误是他们可能能异步运行,就向前面的例子,他们在响应异步事件的时候发生,(例如,定时器,网络中断的硬件事件),就象相同的情况运行1到10次(或者20,1000或任何次)都不会有问题,但在n+1次死锁了。通常,
使情况更糟的是,有些情况他们看起来象死锁,实际上不是,他们可能仅仅在被一些资源组塞在那里需要更长时间的等待,可能是网络连接或硬件中断,这种情况下,你错误的将他们作为死锁并杀死他们,但如果你让它继续,他们可能自己恢复。
为了阻止这些尴尬的情况出现,你应该很好考虑分析你程序中存在的死锁问题。这个问题是本文的重点。
这是一个终所周知的死锁情况。当初始化DOS BOX时,WIN3.1的VMM(虚拟机管理器)发送一个请求给网卡,让它初始化,同时,基于DOS的网络驱动器参与这个黑洞查询网络卡,这是对定时器中断的周期性行为,并且它只在时间溢出或执行成功的时候返回。
不幸的是,由于WIN3.1上的VTD一些微妙的问题,可能在有些情况下,VM不会发现任何定时器中断,结果VM不会从初始化序列返回。另一方面,VMM拒绝映射更多的定时器到新创建的VM中,除非那初始化返回,那么死锁了。
任何死锁中典型的错误是他们可能能异步运行,就向前面的例子,他们在响应异步事件的时候发生,(例如,定时器,网络中断的硬件事件),就象相同的情况运行1到10次(或者20,1000或任何次)都不会有问题,但在n+1次死锁了。通常,
使情况更糟的是,有些情况他们看起来象死锁,实际上不是,他们可能仅仅在被一些资源组塞在那里需要更长时间的等待,可能是网络连接或硬件中断,这种情况下,你错误的将他们作为死锁并杀死他们,但如果你让它继续,他们可能自己恢复。
为了阻止这些尴尬的情况出现,你应该很好考虑分析你程序中存在的死锁问题。这个问题是本文的重点。
这是一个终所周知的死锁情况。当初始化DOS BOX时,WIN3.1的VMM(虚拟机管理器)发送一个请求给网卡,让它初始化,同时,基于DOS的网络驱动器参与这个黑洞查询网络卡,这是对定时器中断的周期性行为,并且它只在时间溢出或执行成功的时候返回。
不幸的是,由于WIN3.1上的VTD一些微妙的问题,可能在有些情况下,VM不会发现任何定时器中断,结果VM不会从初始化序列返回。另一方面,VMM拒绝映射更多的定时器到新创建的VM中,除非那初始化返回,那么死锁了。
任何死锁中典型的错误是他们可能能异步运行,就向前面的例子,他们在响应异步事件的时候发生,(例如,定时器,网络中断的硬件事件),就象相同的情况运行1到10次(或者20,1000或任何次)都不会有问题,但在n+1次死锁了。通常,
使情况更糟的是,有些情况他们看起来象死锁,实际上不是,他们可能仅仅在被一些资源组塞在那里需要更长时间的等待,可能是网络连接或硬件中断,这种情况下,你错误的将他们作为死锁并杀死他们,但如果你让它继续,他们可能自己恢复。
为了阻止这些尴尬的情况出现,你应该很好考虑分析你程序中存在的死锁问题。这个问题是本文的重点。