6

我正在寻找我的程序中可能出现的死锁,并且我怀疑以下情况。如果 2 个线程同时调用 EnterCriticalSection 并且线程#1 在进入后立即调用 DeleteCriticalSection 会发生什么情况,那么仍在 EnterCriticalSection 调用中的线程#2 会发生什么情况?

谢谢。

4

1 回答 1

13

如果 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()或其任何一个亲戚。

于 2012-10-08T01:55:05.977 回答