从非主线程绘制到主窗体画布

2023-12-23

我正在尝试为我的学校项目制作一款街机游戏。基本思想是在主线程之外的其他线程中完成所有数学和绘图,并且仅将主线程用于输入例程。绘图是通过保存在外部单元中的过程完成的,并且是通过创建位图,然后在位图上绘制部分环境,最后在主窗体的画布上绘制位图来完成的。当我完成绘图过程时,我尝试从主线程运行它,并设法使 everythink 按预期工作(除了整个应用程序窗口被冻结的事实,但由于主线程一直在不停地工作,比如这是预期的)。然后我尝试将该过程放入其他线程中,它停止工作(尽管调试例程报告该过程被重复执行,但它没有绘制任何东西)。在添加和删除一些调试例程之后,它开始无明显原因地工作,但不可靠。在大约 80% 的情况下,它运行顺利,但在其余情况下,它会在十到三十帧后停止,有时甚至不会在卡住的最后一帧中拖动某些环境部分。

主窗体单元的重要部分如下所示

procedure TForm1.Button1Click(Sender: TObject);

begin
running:=not running;
if running then AppTheard.Create(false);
end;

Procedure AppTheard.execute;

begin
 form1.Button1.Caption:='running';
 while running do begin view.nextframe; end;
 form1.Button1.Caption:='no longer running';

end;

另一个单元中的下一帧过程如下所示

     Camera = class
 owner:Tform;
 focus:GravityAffected;
 Walls:PBlankLevel;
 Creeps:MonsterList;
 FrameRateCap,lastframe:integer;
 Background:TBitmap;
 plocha:TBitmap;
 RelativePosY,RelativePosX:integer;
 constructor create(owner:Tform; focus:GravityAffected; Walls:PBlankLevel; Creeps:MonsterList; FrameRateCap:integer; background:TBitmap);
 procedure nextframe;
end;    



 procedure camera.nextframe;
 var i,i1,top,topinfield, left,leftinfield: integer ;

  procedure Repair
      //some unimportant math here

  Procedure vykresli(co:vec);
   begin
   if co is gravityaffected then
    plocha.Canvas.Draw(co.PositionX*fieldsize+Gravityaffected(co).PosInFieldX-Left*fieldsize+leftinfield-co.getImgPosX,
     co.PositionY*fieldsize+Gravityaffected(co).PosInFieldY-top*fieldsize+topinfield-co.getImgPosY,
     co.image)
   else
     plocha.Canvas.Draw(co.PositionX*fieldsize-Left*fieldsize+leftinfield-co.getImgPosX,
     co.PositionY*fieldsize-top*fieldsize+topinfield-co.getImgPosY,
     co.image);
   end;

 begin
   // some more unimportant math

  vykresli(focus);                                                 

  For i:= Left+1 to left+2+(plocha.Width div fieldsize) do                                                         //vykreslení zdí
   For i1:= Top+1 to top+2+(plocha.Height div fieldsize) do
    if (i< Walls.LevelSizeX) and (i1< Walls.LevelSizeY) and (i>=0) and (i1>=0) and walls.IsZed(i,i1) then
     begin vykresli(walls^.GiveZed(i,i1)^);end;

  while abs((gettickcount() mod high(word))-lastframe) < (1000 div FrameRateCap) do sleep(1); 
  lastframe:=gettickcount mod high (word);

  owner.Canvas.Draw(-fieldsize,-fieldsize,plocha);     

 end;

有人可以告诉我我做错了什么吗?

编辑:我得到了我所要求的帮助,但几年后,我意识到我真正需要的建议是根本不使用线程并尝试类似的事情this https://stackoverflow.com/questions/5399928/execute-action-every-x-seconds-delphi反而。


我发现你的做法有很多错误。

1) 所有VCL交互必须在主线程内完成

您的线程直接访问 VCL 控件。您不能这样做,因为 VCL 不是线程安全的。您必须将所有事件同步回主线程,并让主线程完成这项工作。

2) 所有自定义 UI 绘图(到表单)必须在表单内部完成OnPaint event.

这解释了为什么它有时有效,有时无效。表单会自动绘制,如果您不使用此事件,您的自定义绘图将由 VCL 绘制。

3)所有UI绘制必须在主线程内完成

这让我们回到第 1 点和第 2 点。VCL 不是线程安全的。您的辅助线程应该只负责执行计算,而不负责绘制 UI。在执行一些计算或完成一些冗长的工作后,您必须将结果同步回主线程,并让该主线程进行绘图。

4)线程应该是完全独立的

您不应该在这个辅助线程中放置任何代码,因为它知道如何显示它。就您而言,您明确引用了该表单。您的线程甚至不应该知道它是否正在被表单使用。你的线程应该只执行冗长的计算工作,并且对用户界面的考虑绝对为零。当您需要指示主窗体重绘时,将事件同步回主窗体。

结论

您需要研究线程安全。通过这样做,您将能够回答您自己的大部分问题。严格设置此线程只是为了处理繁重的工作,否则会使 UI 陷入困境。不必太担心用户界面缓慢,大多数现代计算机都能够在不到一秒的时间内执行复杂的绘图。这不需要在单独的线程中。


EDIT

经过几年的经验,我开始意识到上面的#3 不一定正确。事实上,在许多情况下,这是从线程内执行详细绘图的好方法,但主线程只负责将该图像呈现给用户。

当然,这本身就是一个完整的话题。您需要能够安全地将一个线程中管理的图像绘制到另一个线程。这也需要使用Synchronize.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从非主线程绘制到主窗体画布 的相关文章

  • 如何在多线程C++ 17程序中交换两个指针?

    我有两个指针 pA 和 pB 它们指向两个大的哈希映射对象 当pB指向的哈希图完全更新后 我想交换pB和pA 在C 17中 如何快速且线程安全地交换它们 原子 我是 c 17 的新手 2个指针的原子无等待交换可以通过以下方式实现 inclu
  • 使 Guid 属性成为线程安全的

    我的一个类有一个 Guid 类型的属性 该属性可以由多个线程同时读写 我的印象是对 Guid 的读取和写入不是原子的 因此我应该锁定它们 我选择这样做 public Guid TestKey get lock testKeyLock ret
  • MFC 中位图背景绘制的线程类型

    我有一个 MFC 文档 视图 C 图形应用程序 它将所有绘图都绘制到离屏位图 然后将其复制到 OnDraw 方法中提供的 CDC 指针 在过去的几天里 我一直在寻找将绘图组件放置在单独的工作线程中 这样它就不会停止 GUI 当我执行此操作时
  • 如果 async-await 不创建任何额外的线程,那么它如何使应用程序响应?

    一次又一次 我看到它说使用async await不创建任何额外的线程 这是没有意义的 因为计算机看起来一次做不止一件事的唯一方式是 实际上一次做不止一件事 并行执行 利用多个处理器 通过调度任务并在它们之间切换来模拟它 做一点A 一点B 一
  • Delphi - Indy - 保存 GMail 草稿

    我一直在 Delphi 下使用 Indy 通过 gmail 帐户发送消息 使用 TIdSMTP 和 TIdMessage 组件 这绝对没问题 但是 我的客户请求将消息保存到 DRAFTS 文件夹 以便他在实际发送消息之前对 以编程方式创建的
  • 使用/不使用 delegate() 启动线程

    有什么区别 new Thread new ThreadStart SomeFunc and new Thread delegate SomeFunc 这段代码在我的计算机上给出了奇怪的输出 public class A int Num pu
  • numba 函数何时编译?

    我正在研究这个例子 http numba pydata org numba doc 0 15 1 examples html multi threading http numba pydata org numba doc 0 15 1 ex
  • Delphi 的内存分析工具?

    我建立了一个项目并运行它 然后在 Process Explorer 中查看它 结果发现它在启动时使用的 RAM 比我想象的要多 5 倍 现在 如果我的程序运行得太慢 我会将其连接到分析器并让它告诉我什么正在使用我的所有周期 有没有类似的工具
  • FireMonkey iOS RAD Studio XE2 - 在从 URL 加载的表单上显示图像

    是否可以将 TImage 放置在 iOS 的 FMX 表单上 并将图像 jpg 从 URL 加载到此 TImage 中以在 iOS 应用程序中显示 我尝试过但没有成功 任何正确方向的提示或指出都会受到赞赏 将 TButton TImageC
  • 如果加载 dll 找不到依赖项,有什么方法可以捕获错误吗?

    我正在编写一个 Windows 32 位程序 可以使用多个可能的 dll 之一 所以它尝试依次加载每个 dll 使用SysUtils SafeLoadLibrary如果加载成功 它就会使用该 dll 不幸的是 其中一些 dll 静态链接到其
  • 报告线程进度的最佳方式

    我有一个程序 它使用线程顺序执行耗时的进程 我希望能够监视每个线程的进度 类似于BackgroundWorker ReportProgress ProgressChanged模型确实如此 我不能使用ThreadPool or Backgro
  • 使用 RxJava 限制吞吐量

    我现在遇到的情况很难解释 所以我会写一个更简单的版本来解释这个问题 我有一个Observable from 它发出一系列由ArrayList文件数量 所有这些文件都应上传到服务器 为此 我有一个函数可以完成这项工作并返回一个Observab
  • Delphi Prism 中 TStringList 的替代品。

    我正在将用 Delphi 2007 Net 编写的应用程序迁移到 Delphi Prism 哪个是替换 TStringList 和 TStrings 类的最佳选择 提前致谢 Bye 只需使用 NET 框架中内置的 List 类型 或者字符串
  • 使用 TArray 而不是 Array of T 的原因是什么?

    我正在将遗留的 Delphi 应用程序迁移到 Delphi XE2 我想知道是否有充分的理由替换定义为的数组Array of MyType to TArray
  • System.InvalidCastException:指定的强制转换无效

    使用 WatiN 的自动化正在进行中 使用几个并发线程来测试应用程序 很少有线程失败 日志报告 堆栈跟踪显示以下内容 System InvalidCastException Specified cast is not valid at SH
  • Delphi + Synapse:如何检查我是否仍然连接

    我在用TTCPBlockSocket http synapse ararat cz doc help blcksock TTCPBlockSocket html对于 TCP IP 应用程序 问题是我无法确定连接何时丢失 GetLastErr
  • Qt中正确的线程方式

    我的图像加载非常耗时 图像很大 并且在加载时也完成了一些操作 我不想阻止应用程序 GUI 我的想法是在另一个线程中加载图像 发出图像已加载的信号 然后用该图像重绘视图 我的做法 void Window loadImage ImageLoad
  • Java基于参数的同步(名为互斥锁/锁)

    我正在寻找一种根据接收到的参数来同步方法的方法 如下所示 public synchronized void doSomething name some code 我想要方法doSomething同步基于name参数如下 线程 1 doSom
  • 为什么我的多螺纹嵌件比单螺纹嵌件性能更好?

    我调查了并发性 http docs mongodb org manual faq concurrency how granular are locks in mongodb在 MongoDB 中 显然它使用了数据库级锁定系统 我认为这意味着
  • 在 Delphi 中将对象转换为 OleVariant

    有没有办法在 OleVariant 中传递包装并解开 TObject 后代 我正在尝试跨自动化对象传递 TObject 我知道这不是一个好主意 但我没有更好的选择 该对象将在来自同一自动化 dll 的对象之间传递 如果这有什么区别的话 像这

随机推荐

  • 客户端脚本中的图像亮度检测

    有谁知道是否有一个脚本可以使用客户端脚本来检测图像 包括 HTML 中的暗度 亮度 我基本上希望能够检测背景中使用的图像的亮度 暗 亮 并让 CSS HTML jQuery JS 根据暗或亮 真或假 的变量来调整页面 我知道有可用的服务器端
  • 与react和express(nginx,docker)建立网络套接字通信

    尝试设置 websocket 连接 当我在本地主机环境中时它工作正常 但是一旦我设置了 docker 环境 客户端 react 就很难与 Express 建立 web socket 通信 我应该定义什么网址才能在两者之间打开网络套接字 我试
  • 使用 Python 将 .doc 转换为纯文本

    我正在尝试使用 texttract 将我的 doc 文件转换为纯文本 import textract text textract process path to file extension 但我收到这个错误 AttributeError
  • Lua 模式匹配与正则表达式

    我现在正在学习lua 关于lua中的模式匹配 我在lua org上的lua文档中找到了以下句子 尽管如此 Lua 中的模式匹配是一个强大的工具 并且包含一些难以与标准 POSIX 实现匹配的功能 由于我熟悉 posix 正则表达式 我想知道
  • Plotly:如何设置自定义 xticks

    From 情节性的文档 https plotly com python reference scatter 布局 gt x 轴 gt 刻度值 设置该轴上的刻度值 出现 仅在以下情况下有效tickmode设置为 数组 与使用ticktext
  • Date().toLocaleString() 输出格式在实时服务器和本地主机上不同

    在我的 Nodejs 应用程序中 我需要日期Y m d H i s格式 我使用这个简单的代码 console log new Date toLocaleString 在本地计算机中我得到 2019 1 8 04 14 28这是正确的格式 但
  • 如何设置 Xcode 插件以进行代码自动格式化[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个在 XCode 中自动格式化 Objective C 代码的插件 拥有一组可选的样式格式也会非常有帮助 我的目标是遵循 Go
  • ElementNotVisibleException:消息:尝试单击 YouTube 搜索中的顶部视频时出现元素不可交互错误

    我似乎无法找到一种方法来单击正确的元素以获得我正在寻找的网址 本质上我想点击topYouTube 搜索中的视频 排名最高的返回视频 如何解决 ElementNotInteractableException 元素在 Selenium webd
  • 在 Windows 上运行导入 xmlrpclib 的 Python 脚本?

    我一直使用Linux来编写Python脚本 但现在我必须让其中一个在Windows XP上运行 在这里我是一个初学者 我已在 C Python34 中安装了 Python 3 4 并且我的 Python 脚本位于 E solidworks
  • 无法将 Laravel 连接到 MailChimp (laravel 5.4)

    我必须在我的列表中定义列表 ID 和 MailChimp API 密钥 env文件 我确信两者都很好 即使我没有收到任何错误 但电子邮件未插入我安装的列表中https github com spatie laravel newsletter
  • Excel 中单元格的数值类型是否始终被视为 DOUBLE?

    In VBA 如本规范 http msdn microsoft com en us library ee177324 aspx如图所示 数值可以有多种类型 Double Integer Long LongLong Single Decima
  • 删除 ANTLR 中的左递归

    正如中所解释的删除左递归 https stackoverflow com questions 2652060 removing left recursion 有两种方法可以去除左递归 使用某些程序修改原始语法以删除左递归 本来写语法就没有左
  • 从Android应用程序访问公共Google Drive文件夹而不进行身份验证

    我希望我的应用程序能够从预定义的共享公共 Google 云端硬盘文件夹中读取数据 而无需用户登录或选择 Google 帐户 背景 环境 Using my desktop browser I have created a public fol
  • uWSGI和joblib Semaphore:Joblib将以串行模式运行

    我正在 Docker 容器内的 Flask 应用程序中运行 joblib 以及由supervisord启动的uWSGI 以启用线程的方式启动 Web服务器的启动显示以下错误 unable to load configuration from
  • 如何抑制“避免使用捆绑版本的 Google Play 服务 SDK”警告?

    我在 Android 应用程序中使用 Google Play 服务 因此我的应用程序中有依赖项build gradle compile com google android gms play services 10 2 1 但 Androi
  • 如何从标签函数调用原生 es6 模板字符串替换?

    我正在为模板文字编写一个 es6 标记函数 它首先检查字符串中的条件 如果未找到条件 则仅将模板文字解释为未标记 我很好奇 从我的标签函数中 是否有一种方法可以调用浏览器的本机模板文字函数 我认为这会比我自己实现的功能 Bonue 有了这个
  • 向 Word 文档添加标题?

    我想使用 PowerShell 将自定义标头添加到 doc 文件 我的意思是实际标头 而不是标题 这应该有效 Word New Object ComObject Word Application wdSeekPrimaryHeader 1
  • qDebug() 的错误输出(UTF - 8)

    我正在尝试存储带有特殊字符的字符串 qDebug lt lt ABCg 输出 这里我什至无法输入正确的输出 在 之后缺少一些垃圾 ABCg 我怀疑有一些 UTF 8 Latin1 ASCII 但找不到输出到控制台 文件的设置 我在代码中写的
  • 为什么每次重新部署时都需要刷新连接池?

    我已经通过 Glassfish 成功连接到远程 MySQL 服务器 但是每次更改代码或 XHTML 文件时 我都需要打开 Glassfish 的管理员面板并刷新连接池 否则会出现以下错误我只是刷新页面 有人经历过这个吗 如果需要的话我可以发
  • 从非主线程绘制到主窗体画布

    我正在尝试为我的学校项目制作一款街机游戏 基本思想是在主线程之外的其他线程中完成所有数学和绘图 并且仅将主线程用于输入例程 绘图是通过保存在外部单元中的过程完成的 并且是通过创建位图 然后在位图上绘制部分环境 最后在主窗体的画布上绘制位图来