仅使用临界区进行读/写锁定会导致死锁

时间:2014-12-01 19:54:15

标签: c++ windows multithreading critical-section thread-synchronization

在经过this question with the same title及其答案之后,我想尝试一些只能使用临界区才能真正起作用的东西,因此应该比现有解决方案(使用其他内核对象,如互斥锁或信号量)快得多< / p>

以下是我的读/写锁定/解锁功能:

#include <windows.h>

typedef struct _RW_LOCK 
{
    CRITICAL_SECTION readerCountLock;
    CRITICAL_SECTION writerLock;
    int readerCount;
} RW_LOCK, *PRW_LOCK;

void InitLock(PRW_LOCK rwlock)
{
    InitializeCriticalSection(&rwlock->readerCountLock);
    InitializeCriticalSection(&rwlock->writerLock);
}

void ReadLock(PRW_LOCK rwlock)
{
    EnterCriticalSection(&rwlock->readerCountLock); // In deadlock 1 thread waits here (see description below)
    if (++rwlock->readerCount == 1) 
    {
        EnterCriticalSection(&rwlock->writerLock); // In deadlock 1 thread waits here
    }
    LeaveCriticalSection(&rwlock->readerCountLock);
}

void ReadUnlock(PRW_LOCK rwlock)
{
    EnterCriticalSection(&rwlock->readerCountLock);
    if (--rwlock->readerCount == 0) 
    {
        LeaveCriticalSection(&rwlock->writerLock);
    }
    LeaveCriticalSection(&rwlock->readerCountLock);
}

int WriteLock(PRW_LOCK rwlock)
{
    EnterCriticalSection(&rwlock->writerLock); // In deadlock 3 threads wait here
}

void WriteUnlock(PRW_LOCK rwlock)
{
    LeaveCriticalSection(&rwlock->writerLock);
}

这是一个线程函数。从InitLock (&g_rwLock);调用main后,我创建了五个线程来尝试这些锁。

void thread_function()
{
    static int value = 0;
    RW_LOCK g_rwLock;

    while(1)
    {
        ReadLock(&g_rwlLock);
        BOOL bIsValueOdd = value % 2;
        ReadUnlock(&g_rwlLock);

        WriteLock(&g_rwlLock);
        value ++;
        WriteUnlock(&g_rwlLock);
    }
}

理想情况下,此代码应该永远运行,没有任何问题。但令我失望的是,它并没有总是。有时它会陷入僵局。我编译了这个并在Windows XP上运行它。要使用线程池创建线程,我使用的是第三方库。因此,不能在这里给出所有涉及大量初始化例程和其他东西的代码。

但是要简短地讲述这个故事,我想知道是否有人通过查看上面的代码可以指出这种方法有什么问题?

我在上面的代码中评论过,当发生死锁时,每个线程(五个线程)都会一直等待。 (我通过将调试器附加到死锁进程找到它)

任何输入/建议都会非常好,因为我已经坚持了很长一段时间(因为贪婪使我的代码运行得比以往任何时候都快)。

2 个答案:

答案 0 :(得分:4)

到目前为止发现了两件事:

  • 您初始化每个线程中的关键部分,这是不允许的(行为未定义)
  • 您不能将一个关键部分留在与其进入的部分不同的线程中(&#34;如果一个线程在没有指定的关键部分对象的所有权时调用LeaveCriticalSection,发生错误,可能导致另一个使用EnterCriticalSection的线程无限期地等待。&#34;)

后者适合你看到的僵局。

如果您同时拥有多个阅读器,则无法控制他们调用ReadUnlock的顺序,因此您无法确保第一个线程进入,这是唯一允许调用的线程{ {1}},是最后一个。

答案 1 :(得分:0)

这样就无法正常运行。

  • 让1个线程进入ReadLock(),让它通过++指令,但在进入writer CS
  • 之前暂停它
  • 另一个线程进入WriteLock()并成功进入writerCS

所以现在我们有读者数= 1并同时运行作家。请注意,读者在EnterCriticalSection(&amp; rwlock-&gt; writerLock)上死锁