Как должна быть реализована “Блокировка с двойной проверкой” в Delphi?
-
11-10-2019 - |
Вопрос
В C # следующий код (из это page) может использоваться для ленивого создания экземпляра одноэлементного класса потокобезопасным способом:
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
if (helper == null) {
lock(this) {
if (helper == null)
helper = new Helper();
}
}
return helper;
}
}
Каким был бы эквивалентный потокобезопасный код Delphi?
В статье также упоминаются две проблемы с двойной проверкой блокировки в Java:
- возможно, что новый объект создается до того, как будет сделана вспомогательная ссылка, указывающая на вновь созданный объект, что означает, что создаются два объекта
- возможно, что вспомогательная ссылка делается для указания на блок памяти, пока объект все еще создается, что означает, что будет возвращена ссылка на неполный объект
Таким образом, хотя код версии C # и Java в упомянутой статье выглядят почти идентично, только версия C # работает так, как ожидалось.Что приводит к дополнительному вопросу, существуют ли эти две проблемы также в версии Delphi с двойной проверкой блокировки?
Решение
Используйте System.TMonitor для блокировки экземпляра объекта потокобезопасным способом.
function TFoo.GetHelper(): THelper;
begin
if not Assigned(FHelper) then
begin
System.MonitorEnter(Self);
try
if not Assigned(FHelper) then
FHelper := THelper.Create();
finally
System.MonitorExit(Self);
end;
end;
Result := FHelper;
end;
Для получения дополнительной информации посмотрите на Заблокируйте мой объект ..., пожалуйста! От Allen Bauer.На самом деле, представитель.Я делаю вывод, что из этого следует обратиться к Аллену.
Другие советы
Конечно, всегда стоит помнить об этом Дважды проверенный замок сломан.Оказывается, эта проблема неприменима к модели памяти x86, но ее всегда стоит иметь в виду на будущее.Я уверен, что в какой-то момент появится версия Delphi, которая будет работать на платформе с моделью памяти, подверженной этой проблеме.
Embarcadero начали использовать версию этого шаблона без блокировок с блокировкой compare / exchange.Например:
class function TEncoding.GetUnicode: TEncoding;
var
LEncoding: TEncoding;
begin
if FUnicodeEncoding = nil then
begin
LEncoding := TUnicodeEncoding.Create;
if InterlockedCompareExchangePointer(Pointer(FUnicodeEncoding), LEncoding, nil) <> nil then
LEncoding.Free;
end;
Result := FUnicodeEncoding;
end;
Я понимаю, что это не ответ на вопрос, но на самом деле это не вписывалось в комментарий!