我正在Unity3D中为iOS和Android构建一个人脸检测应用程序。该应用程序应该每秒检测10张人脸。然而,每次人脸检测大约需要50毫秒,所以我决定让人脸检测在另一个线程的后台运行,以防止出现小的滞后峰值(应用程序需要每帧更新视觉内容,50毫秒的停止会使其滞后)。
问题是线程导致应用程序在iOS上滞后。在没有线程的情况下,它过去每秒检测10个面,现在有线程时检测2个。。它在安卓系统上运行良好。
奇怪的是,该应用程序在iOS上的前5秒运行良好。它是否检测到人脸并不重要,5秒后会出现严重的滞后。编辑:如果我暂停应用程序,然后继续它,滞后会像开始时一样再消失5秒。这是什么!?
下面是一个处理人脸检测的协同程序,它每秒运行10次。如果面部检测线程没有在100毫秒内完成,则会阻止另一个线程启动。
IEnumerator FaceDetection()
{
while (true)
{
if (faceDetectionThreadDone)
{
rgbaMat = webCamTextureToMatHelper.GetMat();
if (rgbaMat != null)
{
OpenCVForUnityUtils.SetImage(faceLandmarkDetector, rgbaMat);
faceDetectionThreadDone = false;
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
// heavy computing stuff (50 ms)
List<UnityEngine.Rect> detectResult = faceLandmarkDetector.Detect();
if (detectResult.Count > 0)
{
points = faceLandmarkDetector.DetectLandmark(detectResult[0]);
}
faceDetectionThreadDone = true;
}).Start();
}
}
yield return new WaitForSeconds(0.1f);
}
}
我如何创建新线程是否存在严重问题?我在某个地方读到,一旦它们执行完毕,就会被垃圾收集器清理掉。这可能会造成滞后吗?构建设置设置在高性能等上
编辑:好的,我现在很确定这个问题与内存管理无关。我已经在Xcode中运行了内部探查器,并观察了堆的使用量。当垃圾收集器启动时,性能没有受到影响。
我测试该应用程序的设备:iPhone 6、索尼Z3、三星S4
创建和启动Thread
非常昂贵。每次
Thread(() =>
{
//Code
}).Start();
被调用时,分配了大约1MB的内存。添加内部发生的其他内存分配,例如List
(detectResult)创建。
另外,yield return new WaitForSeconds(0.1f);
分配内存,但我认为这不是问题所在。您可以通过在while循环之前声明WaitForSeconds
,然后使用它来防止这种情况。
例如,
//Create instance of WaitForSeconds once, before the while loop
WaitForSeconds waitForSeconds = new WaitForSeconds(0.1f);
while (true){
//your Thread code
}
yield return waitForSeconds; //No more memory allocation
不要真的认为这是主要问题,但我想让你知道你可以这么做。对于我上面提到的Thread
问题,您应该创建一个while
循环,处理您的数据,然后在主Thread
中执行输出。基本上是一个线程函数,它将永远循环,而不是每次创建一个新的Thread
。
有一个名为Unity Threading Helper的插件,它允许您从另一个线程调用Unity API函数。你可能想使用它。它有付费和免费版本。免费版本中没有包含示例。这是唯一的区别,但你可以在我发布的链接上下载它和示例。
下面是如何使用API的一般示例。阅读代码中的注释。你的工作是把问题中的代码放在合适的位置,这很容易做到
bool keepProcessing;
UnityThreading.ActionThread myThread;
void Start()
{
keepProcessing = true;
//Create Thread once
myThread = UnityThreadHelper.CreateThread(() =>
{
//Will process Data forever!
while (keepProcessing)
{
//Do heavy computing stuff
//Computing Done! Do something with the output data
UnityThreadHelper.Dispatcher.Dispatch(() =>
{
//You can safely call Unity API functions inside this codeblock
}
//Wait so that Unity won't Freeze
Thread.Sleep(1);
}
});
}
void stopProcessing()
{
keepProcessing = false;
myThread.Abort();
}
最后,我建议您重新设计DetectLandmark
函数,将array
作为参数,而不是List
。分配足够的数组内存一次,然后重复使用。这将完全删除应用程序中的内存分配。
在1/30帧中将Application.targetFrameRate设置为15,在29/30帧中将其设置为60解决了问题……
必须是Unity中与vsync相关的错误。。