如果一个线程在关键部分调用 Acquire(),如果另一个线程调用 Release(),该锁是否会被释放?

If a thread calls Acquire() on a critical section, will that lock be freed if a different thread calls Release()?

我正在使用 Embarcadero 的关键部分实现,TCriticalSection, 但他们的文档没有回答我的问题。

如果我们有一个全局临界区对象:

namespace
{
   //delphi style class must be constructed on the heap
   TCriticalSection* criticalSection = new TCriticalSection();
}

//somewhere in thread 1...
criticalSection->Acquire();

//somewhere in thread2...
criticalSection->Release();

线程2中的release调用会不会打开临界区?

我问是因为我有RAII class,它在销毁期间释放锁,所以如果代码抛出异常我们不会进入死锁。然而关键部分只是方法的一部分。

//...
CRITICAL_SECTION_LOCK lock( criticalSection );
OneAtATimePlease();
lock.Release(); 
//...

所以我不想将关键部分放在 OneAtATimePlease() 中,因为那样我们将为代码中对该方法的每次调用处理锁。

"Critical Section"真是个MSWindows的概念。如果一个线程未能解锁关键部分会发生什么是不确定的,如果另一个线程试图解锁它没有锁定的关键部分会发生什么也是不确定的。

你的建议(另一个线程解锁关键部分)是不可能的,因为 locks 是线程局部的,即使关键部分是共享的,因此每个线程只能解开自己的锁。

TCriticalSection 是 Win32 Critical Section object, where Acquire() simply calls EnterCriticalSection(), and Release() simply calls LeaveCriticalSection().

的包装器

LeaveCriticalSection() 文档指出:

A thread uses the EnterCriticalSection or TryEnterCriticalSection function to acquire ownership of a critical section object. To release its ownership, the thread must call LeaveCriticalSection once for each time that it entered the critical section.

If a thread calls LeaveCriticalSection when it does not have ownership of the specified critical section object, an error occurs that may cause another thread using EnterCriticalSection to wait indefinitely.

因此,请勿尝试解锁当前不拥有锁的线程中的临界区。

lock 变量移到 OneAtATimePlease() 中是完全可以的(也是首选),它属于:

void OneAtATimePlease()
{
    CRITICAL_SECTION_LOCK lock( criticalSection );
    ...
}

想想如果多个线程同时调用 OneAtATimePlease() 但一个线程没有锁定关键部分,使用您的原始代码会发生什么情况:

线程 1

CRITICAL_SECTION_LOCK lock( criticalSection );
OneAtATimePlease();
lock.Release();

线程 2

CRITICAL_SECTION_LOCK lock( criticalSection );
OneAtATimePlease();
lock.Release();

线程 3

// NO LOCK!!!
OneAtATimePlease();

线程 3 可以执行 OneAtATimePlease() 线程 1 或 2 已经在其中!这违背了使用临界区的全部目的。如果您将锁移动到 OneAtATimePlease() 内,那么多个线程将无法彼此不同步(除非其中一个线程在不拥有锁时错误地解锁了关键部分,但是您的 RAII 包装器会阻止这种情况。

根据 documentation:

,这甚至可以递归且安全地工作

When a thread owns a critical section, it can make additional calls to EnterCriticalSection or TryEnterCriticalSection without blocking its execution. This prevents a thread from deadlocking itself while waiting for a critical section that it already owns. To release its ownership, the thread must call LeaveCriticalSection one time for each time that it entered the critical section.

void OneAtATimePlease()
{
    CRITICAL_SECTION_LOCK lock( criticalSection );
    ...
    if (some condition)
        OneAtATimePlease();
    ....
}