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

use*_*542 4 winapi multithreading deadlock critical-section

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

谢谢.

In *_*ico 12

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

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

让我们说交错是这样的:

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

在这种情况下,线程#2的状态根据MSDN未定义:

DeleteCriticalSection函数

备注

删除关键节对象会释放对象使用的所有系统资源.

删除临界区对象后,不要在除InitializeCriticalSection和InitializeCriticalSectionAndSpinCount之外的关键区域(如EnterCriticalSection,TryEnterCriticalSection和LeaveCriticalSection)上运行的任何函数中引用该对象.如果尝试这样做,可能会发生内存损坏和其他意外错误.

如果临界区在其仍然拥有时被删除,则等待已删除的临界区的所有权的线程的状态是未定义的.

因此,如果您不幸的是两个线程遇到上述交错,那么操作系统无法保证您的程序将继续按预期工作.例如,这可能包括死锁线程#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();
}
Run Code Online (Sandbox Code Playgroud)

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

    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()或其任何一个亲属.