【问题标题】:Asynchronous threads drawing in Bitmaps Delphi位图 Delphi 中的异步线程绘制
【发布时间】:2013-01-05 11:51:19
【问题描述】:

如果多个异步线程在全局 TBitmap 中绘制,会引发错误吗?我应该使用关键部分创建我的代码吗? (我在网上冲浪时发现 TBitmap.Draw 不是线程安全的)

另一个问题:如果许多同步线程在全局 TBitmap 中绘制,而 VCL Timer 异步读取 TBitmap 中的内容,这会引发错误吗?

谢谢!

【问题讨论】:

  • 您真的需要全局 TBitmap 吗?我现在猜测了一下,但我认为您需要多个 TBitmap 实例。 TTimer 渲染的实例不应是其他线程当前正在写入的实例。
  • 诀窍是,在视频渲染中,保留一个准备好的 TBitmap 队列,以便 TTimer 始终可以将一个队列取出并渲染它而无需任何同步。与忙于准备要排队的其他位图的其他线程一起。在一个位图实例上与互斥锁、临界区等同步会很混乱并且性能很差,即使您可以让它正常工作。如果您可以避免尽可能多的锁定/同步,多线程性能会更安全、更快——线程在不同的位图上工作,而不是正在渲染的位图。
  • @Martin 你不能真正在TTimer 上进行视频渲染。你需要比这更精确的东西。
  • @DavidHeffernan - 你可以!只有当 GUI 线程(以及 TTimer)无法获得足够的 CPU 来跟上帧速率时,才会出现问题。如果是这种情况,那么您使用哪种 VCL/多媒体计时器并不重要,是时候转向 DirectX 等了。
  • @Martin 嗯,我不相信。我认为你需要一个真正的计时器。

标签: multithreading delphi graphics asynchronous bitmap


【解决方案1】:

是的,您确实需要保护TBitmap 免受跨多个线程的并发访问。关键部分可以序列化您的绘图代码,但是这本身还不够!主线程缓存 GDI 资源并定期对其进行清理,这将影响您的TBitmap。因此,您还需要在绘制/渲染时Lock/Unlock()TBitmap.Canvas,以确保 VCL 不会在您背后剥夺其资源。

【讨论】:

  • 我刚刚遇到这个问题,我知道的派对有点晚了。是的,如果您从主线程以外的线程调用任何TBitmap 绘图方法,则TBitmap 泄漏GDI 句柄如果您不使用Lock/Unlock。即使每个位图实例都是线程私有的。因为讨厌的全局资源缓存。真的很讨厌,一个真正糟糕的设计。我的解决方案是使用来自 GR32 的真正线程安全位图,但这可能不是每个人的选择。
【解决方案2】:

由于您的线程都在修改同一个位图,您需要序列化对该位图的所有访问。这意味着读取它的内容以及写入它。

当然,这假设多个线程绘制到共享位图是解决问题的正确方法。在不知道您的实际问题是什么的情况下,我无法对此发表评论。

更新

由于Remy's answer 中描述的问题,您还必须在绘制位图时使用Lock/Unlock。这应该是这个问题的公认答案。

【讨论】:

  • 这是一个不错的选择。另一种选择是同步到主线程。什么是最好的取决于各种因素。
  • @user - 如果你在位图的画布上绘图,它有一个使用临界区的锁定方法。
【解决方案3】:

当您的 TBitmap 像素发生变化时,使用监视器或信号量来控制您的线程!

【讨论】:

    【解决方案4】:

    你能改用 TThread.Synchronize 方法吗?

    http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/Classes_TThread_Synchronize@TThread@TThreadMethod.html

    根据 TThread 类的文档

    以下是使用线程时需要注意的问题和建议:

    跟踪过多的线程会消耗 CPU 时间;建议的限制是单处理器系统上每个进程 16 个活动线程。

    当多个线程更新同一个资源时,必须同步,避免冲突。

    大多数访问对象和更新表单的方法只能在主线程中调用或使用同步对象,例如 TMultiReadExclusiveWriteSynchronizer。

    【讨论】:

    • 我认为如果我使用关键部分,我会提高我的表现。
    • 我正在使用 TRTLCriticalSection;
    • 我没用过这个类,但如果它是类似 TCriticalSection 的东西,那么它就是在使用位图之前获取锁,然后在完成时释放(总是在 try/finally 块中)。假设 TBitmap 是全局的,您最好定义一个包含 TBitmap 和关键部分的自定义类,并将自定义类的实例设为全局。我不希望它提高性能(因为所有这些线程都需要序列化它们的访问),但读/写将是原子的和可靠的。
    • 全局位图是一个糟糕的计划 - 如果可能的话,OP 应该转储它。
    猜你喜欢
    • 1970-01-01
    • 2014-08-14
    • 1970-01-01
    • 2011-01-18
    • 1970-01-01
    • 2012-06-22
    • 1970-01-01
    • 1970-01-01
    • 2019-10-29
    相关资源
    最近更新 更多