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
我正在维护一个使用 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