如何在Unity中检测死循环和卡死
作者:Qing''s Blog 发布时间:2023-12-18 00:55:17
当游戏在手机/模拟器上卡死,logcat没有日志输出,也没有卡死堆栈信息或者bugly也没有捕获到异常,你是否很焦急?本文介绍一下我们项目中检测Unity卡死的方法,也许适合你使用。
实现原理
在绝大多数情况下我们可以认为Unity是单线程的,基于这点我们在Unity的系统函数FixedUpdate中统计游戏运行期间的总帧数,如果Unity没有卡死,那么TotalFrame是会一直累加的,如果在某一段时间内TotalFrame都不会变化了,则可以认为Unity已经卡死了
既然Unity的主线程已经卡死了,我们就需要用另一个线程用来定时检查unity主线程中的TotalFrame是否不会变化了
示例代码
using System;
using System.Threading;
using UnityEngine;
namespace KEngine
{
/// <summary>
/// 开另外一个线程检测unity是否被卡死
/// </summary>
public static class UnityThreadDetect
{
public static Thread _MainThread = System.Threading.Thread.CurrentThread;//获取unity线程
private static int check_interval = 3000;//检测间隔
public static void Start()
{
new Thread(CheckMainThread).Start();
}
static void CheckMainThread()
{
long frame = 0;
while(!AppEngine.IsApplicationQuit)
{
frame = AppEngine.TotalFrame;
Thread.Sleep(check_interval);
if (frame == AppEngine.TotalFrame)
{
Log.LogToFile("unity thread dead,ThreadState:{0}",_MainThread.ThreadState);
if (AppEngine.IsApplicationFocus)
{
//todo report error
}
}
}
}
}
}
捕获卡死的方法名
在我们的游戏中一般出现卡死的情况都是在定时器里面,我们的定时器是通过在Unity的Update驱动定时器列表,当卡死时,在另一个线程中打印出定时器中正在执行的函数就可以定位到卡死的函数了。定时器可参考:UnityTimer中的Timer.cs
同时在Unity的Update进行派发多个事件,比如PreUpdate,Update,以便出问题更容易定位到卡在那儿
举例说明问题
下面举例我们遇到的出现卡死的问题
死循环
下面这个死循环在Unity中会卡死,而在.NET中不会,.NET中当i超过byte的最大值255时i会从0开始
public static void TesBadCode()
{
byte i = 0;
while (true)
{
i++;
}
}
目前我们遇到的绝大多数情况都是逻辑代码中写了where(true) do xxx
然后里面某些情况不会break,导致循环永远退不出来
屏蔽了事件系统
在某些系统中屏蔽掉了UGUI的事件系统,导致无法接受用户输入,这个问题不应该归类为Unity卡死,但用户反馈来看就是卡死了,无法操作。
重复添加定时器
起因是底层没有对同名定时器进行限制,在某些逻辑中误使用,出现每秒添加一个定时器,而定时器中的逻辑很大且长时间不退出的,当不断添加重复定时器就导致游戏运行越来越慢
重复注册事件
在一些界面的刷新函数和控制器函数,被频繁重复注册了事件,导致在抛出事件时,同一个函数被调用了N次,这个问题在Unity的Profiler中可以清晰看到函数的调用次数
扩展
递归调用
递归调用,会报stack overflow,不会让unity卡死
为什么无限循环递归调用不会卡死Unity?
这是因为每个方法的方法调用栈容量
是有限的,当超出之后就会跳出报stack overflow,不会让应用程序卡死
public static void TesBadCode()
{
while (true)
{
TesBadCode();
}
}
来源:https://www.cnblogs.com/zhaoqingqing/p/13556058.html


猜你喜欢
- 本文演示如何在Android中实现ListView圆角效果。无论是网站,还是APP,人们都爱看一些新颖的视图效果。直角看多了,就想看看圆角,
- 前言 之前的文章有介绍ActivityGroup,不少人问嵌套使用的问题,同样的需求在Fragment中也存在,幸好在最新的An
- 使用ApplicationContext获取bean对象编写一个ApplicationContextFactory工厂类public cla
- 小程序获取手机号,后端JAVA解密流程代码微信官方文档获取手机号流程地址,先看下最好方便理解下面步骤实现思路,步骤如下1.前端需先调用官方w
- 目录1、java有8种基本类型,请问byte、int、long、char、float、double、boolean各占多少个字节?2、在 A
- final File imageFile = new File(getCacheDir().getPath() + "/img/&
- mybatis 3 | 参考文档MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几
- RocketMQ发送消息我们在使用RocketMQ发送消息时,一般都会使用DefaultMQProducer,类型的代码如下:Default
- 主要思路就是提供者持有密钥,通过RSA加密客户机标识或时间标识,再通过Base64加密成不太难看的注册码,然后分发给客户机。客户机解Base
- 我们在学习接口的时候。能够在里面做一些方法的调用。不过今天所要讲的JDBC,虽然也是连接数据库的一种接口,不过与类接口有着很大的区别,大家要
- strcpy与memcpy都是标准C库函数,它们有下面的特点。strcpy提供了字符串的复制。即strcpy只用于字符串复制,并且它不仅复制
- Java的源代码是以*.java的纯文本文件,可以使用任何文本编辑器来进行编写,但是这个源代码是无法执行的。执行源代码的这个任务就需要JDK
- 下面通过代码给大家分享下依赖注入框架Autofac的使用,具体如下所示: Autofac是一款IOC框架,比较于其他的IOC框架,
- 本文实例讲述了C#实现获取鼠标句柄的方法,分享给大家供大家参考。具体实现方法如下:一、调用user32.dll(1)引用using Syst
- 本文实例讲述了C#通过链表实现队列的方法。分享给大家供大家参考。具体实现方法如下:public class Node{ public int
- 欢迎大家来学习本节内容,前几节我们已经学习了其他几种自定义控件,分别是Andriod 自定义控件之音频条及 Andriod 自定义控件之创建
- Collection继承、实现关系如下(说明(I)表示接口, (C)表示Java类,<--表示继承,<<——表示实现):(
- 前言最近写了一篇博客是关于使用Jenkins来构建SVN+Maven项目 ,这里使用的的代码版本工具是SVN,但是事实上也有很多公司使用GI
- 冒泡排序在八大排序中,冒泡排序是最为出名的排序算法之一!冒泡排序的代码还是相当简单的,两层循环,外层是冒泡轮数,里层是依次比较,这个算法的时
- 随着使用Spring进行开发的个人和企业越来越多,Spring从一个单一简介的框架变成了一个大而全的开源软件,最直观的变化就是Spring需