FMX.Graphics.TBitmap.Canvas 可以安全地由线程创建吗?

Can FMX.Graphics.TBitmap.Canvas safely be created by threads?

我正在维护一个使用 FMX.Graphics.TBitmap 的 Delphi 10.2.3 Isapi 应用程序。 多个线程正在创建自己的 private 位图,在其上绘制,return 二进制内容到 webrequest 处理程序,并释放位图。 在此堆栈跟踪中发生调试访问冲突时:

:760c4742 KERNELBASE.RaiseException + 0x62
System.DynArraySetLength(nil,7163,16,$F)
System.DynArraySetLength(13648,DDE84,1,B6FE78)
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,22F70))
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;

它正在调用 单例 TMessageManager.DefaultManager 并向其内部字典添加一个处理程序,没有任何锁定。这看起来不是很线程安全。 根据文档,当使用 BeginScene 和 EndScene 时,可以在线程中使用 FMX 位图,这很好。 但实际上 creating/destroying FMX canvas 似乎不是线程安全的,因为 subscribe/unsubscribe 到单例默认 MessageManager?这个假设正确吗?

奇怪的是,只有当任何代码通过断点在调试器中的某处暂停和恢复时,它才可能引发访问冲突。 当程序永远不会被断点暂停时,它将 运行 没有问题。

我确认 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;

如果你看一下tcontext3d的代码,你会发现它使用了密集的全局变量,因此不能多线程!例如 bitmap.canvas.beginscene

时使用 Tcontext3d