Перейти к содержанию
Fire Monkey от А до Я
  • 0

Странное поведение TThread


Alex Bakulin

Вопрос

Есть простая вроде бы задачка - в фоне загружать картинки для ListView. Делаю это так:

procedure TImageThread.Execute;
var
  i: integer;
begin
  for i := 0 to FListView.Items.Count -1 do
  begin
     FimageIndex := i;
     Synchronize(LoadImage);
  end;
end;

Ожидается, что с ListView в это время будет относительно комфортно работать, и в фоне в ImageList будут подгружаться картинки и появляться в ListView. А на деле получается так, что все приложение виснет аццки на время загрузки картинок и не отвечает. И это как под Windows так и под Android.

Ссылка на комментарий

Рекомендуемые сообщения

  • 0
  • Модераторы
8 минут назад, Alex Bakulin сказал:

Есть простая вроде бы задачка - в фоне загружать картинки для ListView. Делаю это так:


procedure TImageThread.Execute;
var
  i: integer;
begin
  for i := 0 to FListView.Items.Count -1 do
  begin
     FimageIndex := i;
     Synchronize(LoadImage);
  end;
end;

Ожидается, что с ListView в это время будет относительно комфортно работать, и в фоне в ImageList будут подгружаться картинки и появляться в ListView. А на деле получается так, что все приложение виснет аццки на время загрузки картинок и не отвечает. И это как под Windows так и под Android.

а что удивительного? вы загружаете в синхронизации, т.е. в главном потоке

Ссылка на комментарий
  • 0
Synchronize(LoadImage);

Эта строка означает, что LoadImage выполняется в главном потоке (для каждой картинки!).

Для примера посмотрите эту тему:

 

Изменено пользователем enatechno
Ссылка на комментарий
  • 0

Мы же работаем с ListView - визуальным компонентом. Не нужно ли синхронизироваться с главным потоком при изменении его свойств?

p.s. Если Synchronize убрать, то мало что меняется. 

Ссылка на комментарий
  • 0
  • Модераторы
1 минуту назад, Alex Bakulin сказал:

p.s. Если Synchronize убрать, то мало что меняется. 

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

Ссылка на комментарий
  • 1
  • Модераторы
procedure LoadBitmapFromURL(const aURL: string; aBitmap: TBitmap;
  const aSuccess: TThreadProcedure = nil; const aError: TThreadProcedure = nil);
var
  thread: TThread;
begin
  thread := TThread.CreateAnonymousThread(
    procedure
    var
      HTTP: THTTPClient;
      Result: TMemoryStream;
    begin
      Result := TMemoryStream.Create;
      HTTP := THTTPClient.Create;
      try
        try
          HTTP.Get(aURL, Result);
          TThread.Synchronize(TThread.CurrentThread,
            procedure
            var
              aSourceBmp: TBitmap;
            begin
              aSourceBmp := TBitmap.Create;
              try
                aSourceBmp.LoadFromStream(Result);
                if not aSourceBmp.IsEmpty then
                begin
                  aBitmap.SetSize(aSourceBmp.Width, aSourceBmp.Height);
                  aBitmap.CopyFromBitmap(aSourceBmp);

                  if Assigned(aSuccess) then
                    aSuccess;
                end;
              finally
                FreeAndNil(aSourceBmp);
              end;
            end);
        except
          TThread.Synchronize(TThread.CurrentThread,
            procedure
            begin
              if Assigned(aError) then
                aError;
            end);
        end;
      finally
        FreeAndNil(Result);
        FreeAndNil(HTTP);
      end;
    end);
  thread.FreeOnTerminate := true;
  thread.start;
end;

в последнее время пользуюсь такой конструкцией

Ссылка на комментарий
  • 0

Еще мультипоточный вариант отсюда

 

class procedure TFireMonkey.CachePictures(const aLV: TListView);
begin
  Pool.SetMaxWorkerThreads(10); // TThreadPool.Default.MaxWorkerThreads

  TParallel.For(0, FMembersList.Count - 1,
    procedure(Idx: Integer)
    var
      AHTTP: TIdHTTP;
    begin
      if not FileExists(FMembersList[Idx].FileName) then
      begin
        AHTTP := TIdHTTP.Create(nil);
        try
          AHTTP.HandleRedirects := true;
          FStreamList[Idx].Clear;
          AHTTP.Get(FMembersList[Idx].URL, FStreamList[Idx]);
        finally
          FreeAndNil(AHTTP);
        end;

        TThread.Queue(TThread.CurrentThread,
          procedure
          var
            AItem: Integer;
          begin
            if FStreamList[Idx].Size > 0 then
            begin
              FStreamList[Idx].SaveToFile(FMembersList[Idx].FileName);
              AItem := Trunc(Idx / aLV.Columns);
              if AItem < aLV.Items.Count then
                aLV.Adapter.ResetView(aLV.Items[AItem]);
            end;
            Log.d('Current Thread = ' + inttostr(Idx));
          end);
      end;
    end, Pool);
end; 

 

После загрузки ставьте boolean флаг в своем массиве, обозначающий что картинка загружена. Btw Boolean у Intel и ARM можно записывать и читать без синхронизации между потоками (Т.е. не нужно использовать TMonitor или AtomicExchange ), если он не в packed структуре (packed record).

Изменено пользователем ENRGY
Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...