如果2个线程执行EnterCriticalSection而线程1执行DeleteCriticalSection会发生什么

时间:2012-10-08 01:49:27

标签: multithreading winapi deadlock critical-section

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

感谢。

1 个答案:

答案 0 :(得分:12)

  

如果两个线程同时调用EnterCriticalSection会发生什么   和线程#1在输入后立即调用DeleteCriticalSection,什么   发生在仍然在EnterCriticalSection调用中的线程#2?

两个线程无法同时进入临界区。那会破坏关键部分的目的。线程#1首先进入临界区,或者线程#2首先进入临界区。你可以在这里玩两种可能的交错。

让我们说交错是这样的:

    Thread 1               Thread 2
    --------               --------
       |                      |
       |                      |
    EnterCS()                 |
    Lock Taken                |
       |                      |
       |                   EnterCS()
       |                   Blocked
       |                      |
       |                      |
    DeleteCS()                |
       |                      |
       |                     ???
       |
      ...

在这种情况下,线程#2的状态为undefined according to MSDN

  

DeleteCriticalSection函数

     

<强>说明

     

删除关键部分对象会释放所有系统   对象使用的资源。

     

删除关键部分对象后,请勿引用   任何在关键部分上运行的函数中的对象(例如   EnterCriticalSection,TryEnterCriticalSection和   LeaveCriticalSection除了InitializeCriticalSection和   InitializeCriticalSectionAndSpinCount。如果你试图这样做,记忆   可能会发生腐败和其他意外错误。

     

如果临界区在仍然拥有时被删除,则线程的状态   等待已删除的关键部分的所有权未定义。

因此,如果你的两个线程遇到上述交错是不够的,那么操作系统会给你无保证你的程序将继续按预期工作。例如,这可能包括死锁线程#2。

但如果交错是这样的:

    Thread 1               Thread 2
    --------               --------
       |                      |
       |                      |
       |                   EnterCS()
       |                   Lock Taken
       |                      |
    EnterCS()                 |
    Blocked                   |
       |                      |
       |                      |
       |                   ExitCS()
       |                   Lock Released
       |                      |
    Unblocked                 |
    LockTaken                 |
       |                      |
    DeleteCS()                |
       |                      |
       |                      |
      ...                    ...

然后很明显,由于线程#1被阻止,它无法删除临界区,直到线程#2离开临界区。然后假设没有其他线程进入临界区,线程#1将能够毫无问题地删除它。

您提出的方案基本上是竞争条件。根据线程的时间安排,它可能正常工作或导致不可预测的问题。在这种情况下,您必须重新构建代码,以便在所有感兴趣的线程释放临界区之后发生对关键部分的破坏。

在这个双线程场景中,一种解决方法是让线程#1离开临界区并等待所有其他线程先完成,然后再删除临界区。像这样的东西,例如:

// Pseudocode for exposition
void Thread1()
{
    EnterCS();
    // Do stuff
    ExitCS();
    WaitForThread2();
    DeleteCS();
}

void Thread2()
{
    EnterCS();
    // Do stuff
    ExitCS();
}

现在,两种可能的交错看起来像这样:

    Thread #2 acquires lock first:  .  Thread #1 acquires lock first:
                                    .
    Thread 1        Thread 2        .   Thread 1        Thread 2
    --------        --------        .   --------        --------
       |               |            .      |               | 
       |            EnterCS()       .   EnterCS()          |
       |            Lock Taken      .   Lock Taken         |
       |               |            .      |               |
    EnterCS()          |            .  // Do stuff      EnterCS()          
    Blocked        // Do stuff      .      |            Blocked
       |               |            .      |               |
       |               |            .   ExitCS()           |
       |            ExitCS()        .   Lock Released      |
       |            Lock Released   .      |               |
       |               |            .      |            Unblocked
    Unblocked          |            .      |            Lock Taken
    Lock Taken         |            .      |               |
       |               |            .      |           // Do stuff
   // Do stuff         |            .      |               |
       |               |            .      |            ExitCs()
    ExitCS()           |            .      |            Lock Released
    Lock Released      |            .      |               |
       |               |            .      |               |
       |               |            .      |               |
    WaitForThread2() --+            .   WaitForThread2() --+
       |                            .      |
    DeleteCS()                      .   DeleteCS()
       |                            .      |
       |                            .      |
      done                          .     done

WaitForThread2()的确切实施取决于您的计划的性质,但肯定会涉及WaitForSingleObject()或其任何一位亲属。