Может ли FMX.Graphics.TBitmap.Canvas безопасно создаваться потоками? - PullRequest
2 голосов
/ 17 октября 2019

Я поддерживаю приложение Delphi 10.2.3 Isapi, которое использует FMX.Graphics.TBitmap. Несколько потоков создают свое собственное личное растровое изображение, рисуют его, возвращают двоичный контент в обработчик веб-запроса и освобождают растровое изображение. Во время отладки нарушения доступа происходят в этой трассировке стека:

:760c4742 KERNELBASE.RaiseException + 0x62
System.DynArraySetLength(nil,$407163,16,$F)
System.DynArraySetLength($6113648,$5DDE84,1,$6B6FE78)
System.Generics.Collections.TListHelper.InternalSetCapacity(8514146)
System.Generics.Collections.TListHelper.InternalGrow(???)
System.Generics.Collections.TListHelper.InternalGrowCheck(???)
System.Generics.Collections.TListHelper.InternalAddManaged((no value))
System.Messaging.TMessageManager.SubscribeToMessage(???,(FMX.Canvas.D2D.TCanvasD2D.ContextLostHandler,$6122F70))
FMX.Canvas.D2D.TCanvasD2D.CreateFromBitmap(???,SystemDefault)
FMX.Graphics.TBitmap.GetCanvas
Unit1.TWorker.Execute

Я подозреваю, что этот код в коде структуры FMX не является потокобезопасным:

// FMX.Canvas.D2D.pas:
constructor TCanvasD2D.CreateFromBitmap(const ABitmap: TBitmap; const AQuality: TCanvasQuality);
begin
  inherited;
  FLastBrushTransform := TMatrix.Identity;
  CreateResources;
  FContextLostId := TMessageManager.DefaultManager.SubscribeToMessage(TContextLostMessage, ContextLostHandler);
end;

Он вызывает singleton TMessageManager.DefaultManager и добавление обработчика во внутренний словарь без какой-либо блокировки. Это не выглядит очень безопасным. Согласно документации, растровые изображения FMX могут использоваться в потоках при использовании BeginScene и EndScene, что нормально. Но на самом деле создание / уничтожение холста FMX не кажется потокобезопасным из-за подписки / отмены подписки на одноэлементный MessageManager по умолчанию? Является ли это предположение правильным?

Странно то, что оно может вызывать нарушения прав доступа только тогда, когда какой-либо код был приостановлен и возобновлен где-то в отладчике через точку останова. Когда программа никогда не останавливается на точках останова, она запускается без проблем.

Ответы [ 2 ]

0 голосов
/ 28 октября 2019

Если вы посмотрите код tcontext3d, вы увидите, что он интенсивно использует глобальные переменные и поэтому не может быть многопоточным! Tcontext3d используется, когда вы делаете, например, bitmap.canvas.beginscene

0 голосов
/ 23 октября 2019

Я подтверждаю, что код в FMX.Graphics.TBitmap.Canvas не является потокобезопасным. просто посмотрите на него, вы, например, увидите, что он прослушивает сообщения, а обмен сообщениями не является потокобезопасным

constructor TD2DBitmapHandle.Create(const AWidth, AHeight: Integer; const AAccess: TMapAccess);
begin
  inherited Create;
  FWidth := AWidth;
  FHeight := AHeight;
  FAccess := AAccess;
  FContextLostId := TMessageManager.DefaultManager.SubscribeToMessage(TContextLostMessage, ContextLostHandler);
end;
...