-1

После прохождения this question with the same title и его ответов я решил попробовать что-то, что должно действительно работать только с использованием критической секции и, следовательно, должно быть намного быстрее, чем существующие решения (которые используют другие объекты ядра тоже как мьютекс или семафор)Блокировка чтения/записи с использованием только критического раздела вызывает тупик

Вот мои чтения/запись блокировка/разблокировка функции:

#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. Чтобы создавать потоки с помощью threadpool, я использую стороннюю библиотеку. Следовательно, не может дать здесь весь этот код, который включает в себя множество инициализирующих подпрограмм и других вещей.

Но, чтобы сократить историю, мне нравится знать, может ли кто-нибудь взглянуть на приведенный выше код, может указать, что не так с этим подходом?

Я прокомментировал в коде выше, где каждая нить (из FIVE threads) продолжает ждать, когда произойдет взаимоблокировка. (Я нашел это, присоединив отладчик к заторможенному процессу).

Любые входы/предложения были бы действительно замечательными, поскольку я застрял над этим в течение довольно долгого времени (в жадности сделать мой код быстрее, чем когда-либо) ,

+0

Обратите внимание, что это блокировка, зависящая от чтения - если ваш счетчик читателей никогда не достигает 0, запись будет ждать вечно. Если это нежелательно, вы можете подумать о системе продажи билетов, как в [Mellor-Crummey-Scott] (http://www.cs.rochester.edu/u/scott/papers/1991_PPoPP_read_write.pdf) " ярмарка ". –

+0

Как обычно: _RW_LOCK - это имя, зарезервированное для компилятора (ведущее подчеркивание, за которым следует заглавная буква). –

+1

Пожалуйста, разместите * актуальный * код, который демонстрирует проблему. Кодекс здесь явно потерял что-то в переводе и тратит время полезного для людей. –

ответ

4

Пятнистый две вещи до сих пор:

  • инициализации критических секций в каждом потоке, что не допускается (поведение не определено)
  • Вы не можете оставить критическую секцию в другом потоке из тот, который вошел в него («Если поток вызывает LeaveCriticalSection, когда он не имеет права собственности на указанный объект критической секции возникает ошибка, которая может привести к другой поток с помощью EnterCriticalSection ждать до бесконечности.»)

Последний подходит к тупику, который вы видите.

После того, как вы одновременно используете несколько считывателей, вы не контролируете, какой заказ они называют ReadUnlock, поэтому вы не можете гарантировать, что первый поток, который является единственным, разрешенным для вызова LeaveCriticalSection, является последним.

+0

Обратите внимание, что каждая нить имеет другую критическую секцию. Очевидно, это не то, что он намеревался, но это означает, что 'LeaveCriticalSection' в отдельном потоке не учитывает тупик. –

+0

@ Коры: о да, хорошо заметили. В этом случае ничто не должно блокировать когда-либо, и есть условие гонки на 'значение'. UB по стандарту, но не будет иметь этих симптомов в Windows. –

+0

На самом деле, подумайте об этом, что такое «Ценность»? Не то же самое, что 'значение'. Поэтому я не знаю теперь, чему мы можем доверять показанному коду, и что мы не можем. Если в реальном коде есть только один критический раздел, возможно, он только один раз вставлен, но мой второй пункт все равно может применяться. –

0

Таким образом, он работает неправильно.

  • позволяет 1 нить ввести() блокировкой чтения, пусть он пройдет ++ инструкции, но паузу перед входом писатель CS
  • другой поток входит (блокировку записи) и успешно вступает writerCS

так что теперь у нас есть число читателей = 1 и работающий автор одновременно.обратите внимание, что считыватель зашел в тупик на EnterCriticalSection (& rwlock-> writerLock)

+0

Это означает, что читатель заблокирован, пока автор не покинет критический раздел. Но что блокирует писателя в тупике, наблюдаемого вопрошающим? –

+0

Возможно, вы правы, я пропустил счетчики. – Anonymous