Заблокируйте свободный несколько читателей единственное устройство записи

Для контроля доступности используйте setHomeAsUpEnabled() .

5
задан Adisak 30 October 2009 в 02:47
поделиться

4 ответа

Я не знаю ни одного подхода MREW без блокировки (или микроблокировки, как в приведенном выше примере), который можно было бы реализовать в коде Intel86.

Для небольших (быстро истекающих) блокировок отлично работает метод вращения из OmniThreadLibrary :

type
TOmniMREW = record
strict private
  omrewReference: integer;      //Reference.Bit0 is 'writing in progress' flag
public
  procedure EnterReadLock; inline;
  procedure EnterWriteLock; inline;
  procedure ExitReadLock; inline;
  procedure ExitWriteLock; inline;
end; { TOmniMREW }

procedure TOmniMREW.EnterReadLock;
var
  currentReference: integer;
begin
  //Wait on writer to reset write flag so Reference.Bit0 must be 0 than increase Reference
  repeat
    currentReference := omrewReference AND NOT 1;
  until currentReference = InterlockedCompareExchange(omrewReference, currentReference + 2, currentReference);
end; { TOmniMREW.EnterReadLock }

procedure TOmniMREW.EnterWriteLock;
var
  currentReference: integer;
begin
  //Wait on writer to reset write flag so omrewReference.Bit0 must be 0 then set omrewReference.Bit0
  repeat
    currentReference := omrewReference AND NOT 1;
  until currentReference = InterlockedCompareExchange(omrewReference, currentReference + 1, currentReference);
  //Now wait on all readers
  repeat
  until omrewReference = 1;
end; { TOmniMREW.EnterWriteLock }

procedure TOmniMREW.ExitReadLock;
begin
  //Decrease omrewReference
  InterlockedExchangeAdd(omrewReference, -2);
end; { TOmniMREW.ExitReadLock }

procedure TOmniMREW.ExitWriteLock;
begin
  omrewReference := 0;
end; { TOmniMREW.ExitWriteLock }

Я только что заметил здесь возможную проблему с выравниванием - код должен проверять, что omrewReference имеет 4 выравнивания. Извещу автора.

6
ответ дан 14 December 2019 в 13:45
поделиться

Просто дополнение - то, что вы здесь видите, обычно известно как Указатели опасности . Понятия не имею, можно ли сделать что-то подобное в Delphi.

0
ответ дан 14 December 2019 в 13:45
поделиться

Прошло некоторое время с тех пор, как я запачкал руки в Delphi, поэтому проверьте это перед использованием, но ... из памяти вы можете получить поведение с подсчетом ссылок, если вы используете интерфейс и реализация с использованием TInterfacedObject.

type
    IDataClass = interface
        function GetSome: integer;
        function GetData: double;

        property Some: integer read GetSome;
        property Data: double read GetData;
    end;

    TDataClass = class(TInterfacedObject, IDataClass)
    private
        FSome: integer;
        FData: double;
    protected
        function GetSome: integer;
        function GetData: double;
    public
        constructor Create(ASome: integer; AData: double);
    end;

Затем вместо этого вы делаете все свои переменные типа ISomeData (смешивание ISomeData и TSomeData - очень плохая идея ... вы легко получаете проблемы со счетчиком ссылок).

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

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

0
ответ дан 14 December 2019 в 13:45
поделиться

У меня есть для вас потенциальное решение; это позволяет новым читателям начать работу в любое время, пока писатель не захочет писать. Затем писатель ждет, пока читатели закончат, и выполняет запись. После того, как письмо будет написано, читатели могут прочитать еще раз.

Кроме того, это решение не требует блокировок или мьютексов, но требует атомарной операции проверки и установки. Я не знаю Delphi и написал свое решение на Lisp, поэтому постараюсь описать его псевдокодом.

(CAPS - это имена функций, все эти функции принимают и не возвращают аргументов)

integer access-mode = 1; // start in reader mode. 

WRITE  loop with current = accessmode, 
            with new = (current & 0xFFFFFFFe) 
            until test-and-set(access-mode, current to new)
       loop until access-mode = 0; 

ENDWRITE assert( access-mode = 0)
         set access-mode to 1

READ loop with current = ( accessmode | 1 ),
          with new = (current + 2),
          until test-and-set(access-mode, current to new)
ENDREAD loop with current = accessmode
             with new = (current - 2),
             until test-and-set(access-mode, current to new)

Для использования считыватель вызывает READ перед чтением и ENDREAD по завершении. Одинокий писатель вызывает WRITE перед записью и ENDWRITE по завершении.

Идея состоит в том, что целое число, называемое режимом доступа, содержит логическое значение в младшем бите и счетчик в старшие биты. WRITE устанавливает бит в 0, а затем вращается до тех пор, пока режим доступа ENDREAD не станет достаточным для обратного отсчета до нуля. Endwrite устанавливает режим доступа обратно в 1. ЧИТАТЬ ИЛИ записывает текущий режим доступа с 1, так что их проверка и установка пройдут, только если младший бит был высоким с самого начала. Я складываю и вычитаю на 2, чтобы оставить младший бит в покое.

Чтобы подсчитать количество читателей, просто сдвиньте режим доступа вправо на единицу.

0
ответ дан 14 December 2019 в 13:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: