Безопасен ли этот код - PullRequest
       14

Безопасен ли этот код

4 голосов
/ 26 апреля 2011
// experimental code
procedure TFormMain.MyThumbnailProvider( const Path: Unicodestring; Width,
 Height: Integer; out Bitmap: TBitmap );
var
   AExtension: string;
   ARect: TRect;
begin
  AExtension := LowerCase( ExtractFileExt( Path ) );
  if AExtension = '.wmf' then
  begin
    ARect.Left := 0;
    ARect.Top := 0;
    ARect.Right := Width;
    ARect.Bottom := Height;
    Image1.Picture.LoadFromFile( Path ); // added at design time to form
    Bitmap := TBitmap.Create;
    Bitmap.Width := Width;
    Bitmap.Height := Height;
    Bitmap.Canvas.StretchDraw( ARect, Image1.Picture.Graphic );
  end;
end;

Отредактировано

procedure TFormMain.MyThumbnailProvider( const Path: Unicodestring; Width, Height: Integer; out Bitmap: TBitmap );
var
  ARect: TRect;
  APicture: TPicture;
  AExtension: string;
begin
  // experimental code
  if FileExists( Path ) then
  begin
    AExtension := LowerCase( ExtractFileExt( Path ) );
    if AExtension = '.wmf' then
    begin
      ARect.Left := 0;
      ARect.Top := 0;
      ARect.Right := Width;
      ARect.Bottom := Height;
      APicture := TPicture.Create;
      try
        APicture.LoadFromFile( Path );
        Bitmap := TBitmap.Create;
        Bitmap.SetSize( Width, Height );
        Bitmap.IgnorePalette := True;
        Bitmap.PixelFormat := pf24bit;
        Bitmap.Transparent := False;
        Bitmap.Canvas.Lock; **// New**
        try
          Bitmap.Canvas.StretchDraw( ARect, APicture.Graphic );
        finally
          Bitmap.Canvas.Unlock;  **// New!**
        end;
      finally
        APicture.Free;
      end;
    end;
  end;
end;

Кажется, это полностью решает проблему рисования!Очевидно, вам нужно блокировать и разблокировать холст при использовании Draw или StretchDraw, потому что в потоке DC его Bitmap.canvas иногда очищается из-за механизма кэширования объектов GDI в graphics.pas.

См. http://qc.embarcadero.com/wc/qcmain.aspx?d=55871

Ответы [ 2 ]

11 голосов
/ 26 апреля 2011

Нет, из-за этого:

Image1.Picture.LoadFromFile( Path );
/// [...]
Bitmap.Canvas.StretchDraw( ARect, Image1.Picture.Graphic );

Вы можете работать только с элементами управления VCL из основного потока VCL.

2 голосов
/ 26 апреля 2011

Как правило, код VCL не является потокобезопасным, и это относится к большинству объектов VCL, доступных для использования.

Вы сказали:

Похоже, что это потокобезопасно, поскольку в потоке не создаются исключения, но изображения кажутся частично пустыми или отображаются неправильно?

«Нет исключений» не являются признаком «безопасности потока». Это то же самое, что сказать: «Я поехал на работу и не разбился, поэтому моя машина защищена от столкновений».

Проблемы с многопоточностью сильно зависят от времени и проявляются разными способами - не только в исключениях. Важно помнить, что проблемы с многопоточностью могут существовать как скрытые дефекты в течение нескольких месяцев, прежде чем произойдет что-то плохое. И даже в этом случае их, как правило, очень трудно воспроизвести с какой-либо степенью согласованности.

  • На самом деле вам повезло, если вы получаете исключения из-за проблем с многопоточностью, другие проблемы могут быть сложнее отследить, или даже понять, что они возникают.
  • Вы можете получить взаимоблокировки, но если это в фоновом потоке, вы можете даже не осознавать этого.
  • Неверное поведение (как вы сообщаете), как правило, из-за условий гонки, в которых:
    • Некоторый код будет взаимодействовать с объектом, пока он находится в несовместимом состоянии - обычно это приводит к крайне непредсказуемому поведению.
    • Данные неправильно «отбрасываются», потому что одна подпрограмма изменяется немедленно, перезаписывая другую.
  • плохая производительность; да, плохо реализованные многопоточные решения могут серьезно снизить производительность.

Когда вы говорите «изображения кажутся частично пустыми или неправильно рисуются», возникает важный вопрос: всегда ли одни и те же изображения плохо себя ведут, одинаково? Если это так, то проблема может заключаться в том, что элемент управления, используемый для загрузки изображений, имеет проблему с этими конкретными файлами.

Вы на самом деле запускаете несколько потоков? Я не увидел в вашем коде ничего, что бы указывало на это.
Вы пытались запустить однопоточный, чтобы убедиться, что это действительно проблема с многопоточностью?


EDIT
Тогда самое простое решение, вероятно, будет:

  • Определите пользовательский констант сообщения, на котором вы можете реализовать обработчик сообщений.
  • Реализация обработчика сообщений для сообщения
  • Измените существующий procedure TFormMain.MyThumbnailProvider, чтобы он мог синхронизироваться с основным потоком VCL и передавал работу синхронизированному обработчику.

Следующие вызовут ваш пользовательский обработчик в главном потоке VCL и будут ждать возврата.

procedure TFormMain.MyThumbnailProvider( const Path: Unicodestring; 
  Width, Height: Integer; out Bitmap: TBitmap );
var
  LThumnailData: TThumbnailData; //Assuming an appropriately defined record
begin
  LThumbnailData.FPath := Path;
  LThumbnailData.FWidth := Width;
  LThumbnailData.FHeight := Height;
  LThumbnailData.FBitmap := nil;
  SendMessage(Self.Handle, <Your Message Const>, 0, Longint(@LThumbnailData));
  Bitmap := LThumbnailData.FBitmap;
end;

EDIT2
Требуется больше образца кода:
Объявление сообщения const.

const
  //Each distinct message must have its own unique ref number.
  //It's recommended to start at WM_APP for custom numbers.
  MSG_THUMBNAILINFO = WM_APP + 0;

Объявление типа записи. Действительно просто, но вам нужен указатель тоже.

type
  PThumbnailData = ^TThumbnailData;
  TThumbnailData = record
    FPath: Unicodestring;
    FWidth, FHeight: Integer;
    FBitmap: TBitmap;
  end;

Объявление обработчика сообщений.

procedure MSGThumbnailInfo(var Message: TMessage); message MSG_THUMBNAILINFO;

Реализация обработчика сообщений.

procedure TForm3.MSGThumbnailInfo(var Message: TMessage);
var
  LThumbnailData: PThumbnailData;
begin
  LThumbnailData := Pointer(Message.LParam);

  //The rest of your code goes here.
  //Don't forget to set LThumbnailData^.FBitmap before done.

  Message.Result := 0;
  inherited;
end;
...