Не могу придумать внятный алгоритм, прошу совета сообщества.
Есть некая долго выполняющаяся функций с одним аргументом, которая имеет внутренний кэш результатов вычислений, и вызываемая из множества потоков. Необходимо реализовать алгоритм, по которому при обращении к функции с одним и тем же аргументом из нескольких потоков одновременно реальные вычисления происходили только в первом обратившемся потоке, а остальные ожидали результата из кэша. Мне видится примерно следующее. Функция пытается создать именованный объект синхронизации на основе хеша от аргумента функции, например mutex. Если он создается успешно, то считаем, что это идет обращение от первого потока, и соответственно читаем результат из кэша, а при отсутствии производим вычисления и заносим их в кеш, удаляем mutex. Если при создании mutex имеем ошибку ERROR_ALREADY_EXISTS, то считаем, что в данный момент производятся вычисления, и ждем освобождения mutex, после чего возвращаемся на исходную позицию. Код:
function GetResultFromCache(AParam: DWORD; out AResult: DWORD): Boolean;
begin
CacheLock.Enter;
try
...
finally
CacheLock.Leave;
end;
end;
procedure AddResultToCache(AParam: DWORD; AResult: DWORD);
begin
CacheLock.Enter;
try
...
finally
CacheLock.Leave;
end;
end;
function Calc(AParam: DWORD): DWORD;
const
MutexName = 'test';
var
Handle: THandle;
ErrCode: DWORD;
begin
while True do
begin
if GetResultFromCache(AParam, Result) then Exit;
Handle := CreateMutex(nil, True, PChar(MutexName));
ErrCode := GetLastError;
if (Handle <> 0) and (ErrCode = 0) then
begin
try
// Calculation
Sleep(5000);
Result := 0;
AddResultToCache(AParam, Result);
finally
CloseHandle(Handle);
end;
Exit;
end
else
if (Handle <> 0) and (ErrCode = ERROR_ALREADY_EXISTS) then
begin
WaitForSingleObject(Handle, INFINITE);
CloseHandle(Handle);
end
else
raise Exception.Create('Непонятная ошибка');
end;
end;
Подскажите, есть у меня ошибки в алгоритме, может быть что-нибудь можно улучшить?