我正在开发一款 Sprite Kit 游戏,我需要进行一些多线程处理以维持健康的 fps。
在更新时,我调用一个函数来创建大量 UIBezierPaths 并使用 C++ 静态库合并它们。
如果我有超过 10 个形状,帧速率会急剧下降,所以我决定尝试 GCD 并尝试使用单独的线程来解决这个问题。
我把它放在 didMoveToView 中:
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
在每一帧调用的函数中,我称之为:
dispatch_async(queue,^(void){[self heavyCalculationsFunc];});
对于非常了解 GCD 的人来说,它在每一帧上创建一个新线程可能是显而易见的,但对我来说还不清楚。
我的问题是,有什么方法可以重用我想在更新时调用的线程吗?
提前感谢您的帮助!
最佳答案
如果您需要在每一帧上完成工作,并且需要在渲染帧之前完成这些工作,那么多线程可能帮不了您,除非您愿意为此付出很多努力。
维持帧速率的关键在于时间——与 CPU 资源无关,只是等待时间。为了保持 60fps 的帧率,你有 16.67 毫秒的时间来完成所有的工作。(实际上,少于这个时间,因为 SpriteKit 和 OpenGL 需要一些时间来渲染你的工作结果。)这是一个同步问题 - 你有工作,您有特定的时间来完成它,因此提高绩效的第一步是减少工作量或提高工作效率。
另一方面,多线程通常用于异步问题 — 您需要做一些工作,但不需要现在完成,因此您可以继续处理其他问题你现在需要做的事情(比如在 16 毫秒内从你的更新方法返回以保持你的帧率)并稍后检查该工作的结果(比如,在后面的帧)。
不过,这两个定义之间有一点回旋余地:几乎所有的现代 iOS 设备都有多核 CPU,所以如果你玩得对,你可以在你的同步问题中加入一点异步性 并行化您的工作量。完成并做好这件事绝非易事——它一直是 serious research and investment by big game studios 的主题。多年来。
看看"How a Scene Processes Frames of Animation"下的图在 SpriteKit 编程指南中。那是你的 16 毫秒时钟。浅蓝色区域是 Apple 的 SpriteKit(以及 OpenGL 和其他系统框架)代码负责的那 16 毫秒的片段。其他切片是你的。让我们展开该图以获得更好的外观:
如果您在任何这些切片中做太多工作,或者使 SpriteKit 的工作负载太大,整个事情就会超过 16 毫秒,并且您的帧率会下降。
线程的机会是在同一时间线内在另一个 CPU 上完成一些工作。如果 SpriteKit 对 Action 、物理和约束的处理不依赖于这项工作,您可以与这些事情并行进行:
或者,如果您的工作需要在 SpriteKit 运行 Action 和物理之前进行,但是您还有其他工作需要在 update 方法中完成,您可以将一些工作发送到另一个线程在完成剩余的 update 工作时,然后在 update 方法中检查结果:
那么如何完成这些事情呢?这是一种使用调度组的方法,并假设 Action /物理/约束不依赖于您的背景工作——这完全不在我的脑海中,所以它可能不是最好的。 :)
// in setup
dispatch_queue_t workQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t resultCatchingGroup = dispatch_group_create();
id stuffThatGetsMadeInTheBackground;
- (void)update:(NSTimeInterval)currentTime {
dispatch_group_async(group, queue, ^{
// Do the background work
stuffThatGetsMadeInTheBackground = // ...
});
// Do anything else you need to before actions/physics/constraints
}
- (void)didFinishUpdate {
// wait for results from the background work
dispatch_group_wait(resultCatchingGroup, DISPATCH_TIME_FOREVER);
// use those results
[self doSomethingWith:stuffThatGetsMadeInTheBackground];
}
当然,正如其名称所示,dispatch_group_wait 会阻止执行以等待您的后台工作完成,因此您仍然有 16 毫秒的时间限制。如果前台工作(您的 update 的其余部分,加上 SpriteKit 的 Action /物理/约束工作以及您为响应这些事情而完成的任何其他工作)在您的后台工作之前完成,您会等着它。如果后台工作加上 SpriteKit 的渲染工作(加上您在生成后台工作之前在 update 中所做的任何事情)花费的时间超过 16 毫秒,您仍然会丢帧。因此,这样做的诀窍是足够详细地了解您的工作量,以便很好地安排它。
关于ios - Sprite Kit 中可重用的多线程实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28748774/
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
我正在尝试使用ruby编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?