Синхронизация при обращении к функции, кеширующей результат
От: Aniskin  
Дата: 23.04.16 07:51
Оценка:
Не могу придумать внятный алгоритм, прошу совета сообщества.

Есть некая долго выполняющаяся функций с одним аргументом, которая имеет внутренний кэш результатов вычислений, и вызываемая из множества потоков. Необходимо реализовать алгоритм, по которому при обращении к функции с одним и тем же аргументом из нескольких потоков одновременно реальные вычисления происходили только в первом обратившемся потоке, а остальные ожидали результата из кэша. Мне видится примерно следующее. Функция пытается создать именованный объект синхронизации на основе хеша от аргумента функции, например 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;


Подскажите, есть у меня ошибки в алгоритме, может быть что-нибудь можно улучшить?
Отредактировано 23.04.2016 16:34 Aniskin . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.