Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
TMyClass = class
private
FInterfaceField: Pointer;
function GetInterfaceField: IMyInterface;
procedure SetInterfaceField(const Value: IMyInterface);
property InterfaceField: IMyInterface read GetInterfaceField write SetInterfaceField;
end;
TMyClass.GetInterfaceField: IMyInterface;
begin
Result := IMyInterface(FInterfaceField);
end;
TMyClass.SetInterfaceField(const Value: IMyInterface);
begin
FInterfaceField := Pointer(Value);
end;
И пользуются после этого свойством.if (FIsDestroying) then exit;Перед циклом, который файрит эвенты, копирую список вызываемых функций в массив, и только потом вызываю делегаты из массива. У каждого паблишера своя критическая секция.
function TBasePublisher.GetItems: TWeakRefArr;
var i: Integer;
begin
if assigned(FCS) then
begin
FCS.Enter;
try
SetLength(Result, FCount);
for i := 0 to FCount - 1 do
Result[i] := FItems[i];
finally
FCS.Leave;
end;
end
else
Result := FItems;
end;
Критическая секция создается если класс создается как ThreadSafe в конструкторе:constructor TBasePublisher.Create(const AThreadSafe: Boolean);
begin
if AThreadSafe then FCS := TCriticalSection.Create;
end;
Ну то есть я делаю ровно так же, как вы только что описали ;)Не-не-не, я не отпускаю критическую секцию при вызове делегатов.Сценарий дедлока возникает легко. Допустим у нас есть VCL и не основной поток, который файрит нотификации. Итак он сфайрил нотификацию, и вызов попадает в обработчик:
procedure TSubscriber.OnBlaBla(Sender: IUnknown)
begin
//что то делаем
//и вызваем синхронизацию в основной поток для работы с VCL
Synchronize(DoSomething);
//если кто-то в процессе работы DoSomething вызовет Subscribe/Unsubscribe,
//или создаст ситуацию когда сфайрится евент из основного потока - это будет гарантированный дедлок
end;
Увы, я слишком много стрелял себе в ногу дедлоками и сразу вижу потенциально опасные места. Единственно безопасный вариант — это не держать критическую секцию у паблишера заблокированной, когда файрятся евенты.if (FIsDestroying) then exit;if (weakptr.IsAlive) then ptr := weakptr.Get;FDestroyCS.Enter;
try
if (FIsDestroying) then exit;
//process notification
finally
FDestroyCS.Leave;
end;
И получение указателя из IWeakly — тоже в этой секции?Нет, зачем же? Получение указателя из IWeakly выполняется паблишером. Ниже написал как многопоточный случай должен обрабатываться.
Трюки с интерфейсами в Delphi