我正在寻找我的程序中可能出现的死锁,并且我怀疑以下情况。如果 2 个线程同时调用 EnterCriticalSection 并且线程#1 在进入后立即调用 DeleteCriticalSection 会发生什么情况,那么仍在 EnterCriticalSection 调用中的线程#2 会发生什么情况?
谢谢。
我正在寻找我的程序中可能出现的死锁,并且我怀疑以下情况。如果 2 个线程同时调用 EnterCriticalSection 并且线程#1 在进入后立即调用 DeleteCriticalSection 会发生什么情况,那么仍在 EnterCriticalSection 调用中的线程#2 会发生什么情况?
谢谢。
如果 2 个线程同时调用 EnterCriticalSection 并且线程#1 在进入后立即调用 DeleteCriticalSection 会发生什么情况,那么仍在 EnterCriticalSection 调用中的线程#2 会发生什么情况?
两个线程不能同时进入临界区。这将破坏关键部分的目的。要么 Thread #1 先进入临界区,要么 Thread #2 先进入临界区。这里有两种可能的交错。
假设交错是这样的:
线程 1 线程 2 -------- -------- | | | | 输入CS() | 锁定 | | | | 输入 CS() | 被封锁 | | | | 删除CS() | | | | ??? | ...
在这种情况下,线程#2 的状态根据 MSDN 是未定义的:
DeleteCriticalSection 函数
评论
删除临界区对象会释放该对象使用的所有系统资源。
删除临界区对象后,请勿在除 InitializeCriticalSection 和 InitializeCriticalSectionAndSpinCount 之外的任何对临界区进行操作的函数(如 EnterCriticalSection、TryEnterCriticalSection 和 LeaveCriticalSection)中引用该对象。如果您尝试这样做,可能会发生内存损坏和其他意外错误。
如果一个临界区在它仍然拥有的时候被删除,那么等待被删除临界区所有权的线程的状态是未定义的。
因此,如果您的两个线程不幸遇到上述交错,那么操作系统无法保证您的程序将继续按预期工作。例如,这可能包括死锁线程#2。
但如果交错是这样的:
线程 1 线程 2 -------- -------- | | | | | 输入 CS() | 锁定 | | 输入CS() | 封锁 | | | | | | 退出CS() | 锁已释放 | | 畅通| 锁定 | | | 删除CS() | | | | | ……
那么显然,由于线程#1 被阻塞,它不能删除临界区,直到线程#2 离开临界区。然后假设没有其他线程进入临界区,线程#1 将能够毫无问题地删除它。
您提出的方案本质上是一种竞争条件。根据线程的时间,它可能工作得很好或导致不可预知的问题。在这种情况下,您必须重新构建代码,以便在所有感兴趣的线程都释放了临界区之后销毁临界区。
在这种双线程场景中,修复它的一种方法是让 Thread #1 离开临界区并等待所有其他线程首先完成,然后再删除临界区。像这样的东西,例如:
// Pseudocode for exposition
void Thread1()
{
EnterCS();
// Do stuff
ExitCS();
WaitForThread2();
DeleteCS();
}
void Thread2()
{
EnterCS();
// Do stuff
ExitCS();
}
现在,两种可能的交错看起来像这样:
线程 #2 首先获取锁: . 线程 #1 首先获取锁: . 线程 1 线程 2。线程 1 线程 2 -------- -------- 。-------- -------- | | . | | | 输入 CS() 。输入CS() | | 锁定。锁定 | | | . | | 输入CS() | . // 做一些事情 EnterCS() 阻塞 // 做事。| 被封锁 | | . | | | | . 退出CS() | | 退出CS() 。锁定释放 | | 锁已释放。| | | | . | 畅通 畅通| . | 锁定 锁定 | . | | | | . | // 做东西 // 做事 | . | | | | . | 退出Cs() 退出CS() | . | 锁已释放 锁定释放 | . | | | | . | | | | . | | WaitForThread2() --+ 。WaitForThread2() --+ | . | 删除 CS() 。删除 CS() | . | | . | 完毕 。完毕
的确切实现WaitForThread2()
将取决于您的程序的性质,但肯定会涉及WaitForSingleObject()
或其任何一个亲戚。