Блокируйте и разблокируйте ресурсы с помощью одной команды

Я работаю с потоками, и поэтому использую мьютексы для блокировки общих ресурсов. Основное использование блокировки - поместить ресурсы в блок блокировки / разблокировки.

procedure RefreshData;
begin    
   DataLock;
      GetData;
      GetSettings;
      CheckValues;
      ...
   DataUnlock;
end;

Поскольку всегда есть пара Lock / Unlock, я начал думать об упрощенном подходе блокировки / разблокировки, который автоматически разблокировал бы ресурсы, когда они больше не нужны.

Итак, моя идея заключалась в том, чтобы ввести новую процедуру, которая будет принимать в качестве входного параметра ссылку на прецедент. Это даст мне возможность использовать анонимный метод.

Код будет примерно таким:

type TBaseProc = reference to procedure;

procedure TMyObject.LockMethod(AMeth: TBaseProc);
begin
  DataLock;
  try
    AMeth;
  finally
    DataUnlock;
  end;
end;


procedure TForm1.RefreshData;
begin
  MyObject.LockMethod(
   procedure 
   begin
     GetData;
     GetSettings;
     CheckValues;
     ...
   end;
  );

end;

Есть ли смысл в таком подходе или есть лучшее или даже более простое решение?

Спасибо и привет.


person Nix    schedule 20.11.2014    source источник
comment
Тебя волнует производительность или нет?   -  person David Heffernan    schedule 20.11.2014
comment
Самым простым решением было бы, если бы компилятор имел встроенную поддержку оператора Lock (), как в C #: msdn.microsoft.com/en-us/library/c5kehkcz.aspx. Лично я использую шаблоны кодов для пар блокировки / разблокировки.   -  person iPath ツ    schedule 20.11.2014
comment
@ Дэвид: Да, я забочусь о производительности. Есть ли какая-нибудь оценка того, сколько накладных расходов может вызвать такой подход? Думаю, пара Lock / Unlock - все же лучший подход с точки зрения производительности.   -  person Nix    schedule 20.11.2014
comment
Вот nice approach с интерфейсами.   -  person TLama    schedule 20.11.2014
comment
В дополнение к этому ... просто короткий вопрос. При вложении блоков блокировки / разблокировки в один поток, например: mtx1.Acquire; mtx1.Acquire; DoSomeStuff; mtx1.release; mtx1.release. Насколько мне известно, блокировка, когда мьютекс уже принадлежит одному и тому же потоку, не произойдет, но как обстоят дела с выпуском? Какой выпуск разблокирует ресурсы, первый или второй вызов? Я не уверен, но я думаю, что где-то читал, что в системе есть внутренний счетчик, поэтому я предполагаю, что последний выпуск действительно разблокирует ресурсы. Я прав?   -  person Nix    schedule 20.11.2014
comment
Я бы никогда не подумал об использовании чего-то другого, кроме простой попытки, наконец, здесь. Все остальные параметры имеют скрытую команду try / finally, которая непрозрачна, а все остальные параметры требуют накладных расходов. Вы будете вынуждены выполнить выделение кучи, что является серьезной головной болью при многопоточности.   -  person David Heffernan    schedule 20.11.2014
comment
Что касается другого вопроса, не задавайте новых вопросов в комментариях. Задайте новый вопрос. И проясните, какой тип мьютекса вы используете. Это рекурсивный мьютекс? Если это так, то на самом деле выпускается последний выпуск. Никакой другой путь не имеет смысла.   -  person David Heffernan    schedule 20.11.2014


Ответы (1)


Этот подход далек от совершенства, потому что, как я понял из вашего кода, у вас всего одна блокировка на все приложение. Лучше, когда у каждого независимого объекта данных будет своя блокировка. Итак, у вас будет такой абстрактный класс:

type
  TAbstractData = class
    private
       CriticalSection: TRtlCriticalSection
    public
       constructor Create;
       procedure Lock;
       procedure Unlock;
       destructor Destroy; override;
  end;

Затем наследуйте другие классы от этого абстрактного класса, реализующего блокировку.

       constructor TAbstractData .Create;
       begin
         inherited Create;
         InitializeCriticalSection(CriticalSection);
       end;

       procedure TAbstractData.Lock;
       begin
         EntercriticalSection(CriticalSection);
       end;

       procedure TAbstractData.Unlock;
       begin
         LeaveSection(CriticalSection);
       end;

       procedure TAbstractData.Destroy; 
       begin
         DeleteCriticalSection(CriticalSection);
         inherited Destroy;
       end;

CriticalSection - это самый эффективный класс синхронизации, реализованный на данный момент в Windows. Это практически бесплатно - почти не потребляет системные ресурсы, если нет конкуренции потоков, и не вызывает дорогостоящее переключение контекста, когда данные использует только один поток.

Отправив ответ, я нашел в Интернете хорошую статью - http://blog.synopse.info/post/2016/01/09/Safe-locks-for-multi-thread-applications - автор рекомендует аналогичный подход.

person Maxim Masiutin    schedule 14.07.2017