Unity3d报奇怪的错误CompareBaseObjectsInternal can only be called from the main thread.

2022/02更新:

多年后看到自己这篇博客,当年看样子没讲清楚。学艺不精,如有误导深感歉意。

Unity基于游戏对象GameObject的编程方式对多线程支持有一定限制,有部分情况下分支线程功能受限。如果是值类型数据(例如float,int,string)可以在分支线程和主线程之间共享访问。如果涉及到对继承MonoBehaviour对象的访问,必须在主线程里面进行。

一般可以做个数据缓存,分支线程如果传回的数据先缓存,然后等到下一次Update方法执行的时候再取出数据进行处理。可以用bool类型做缓存数据是否已经处理的flag标记,但最好是采用加锁方式保证主、分线程之间没有冲突(如果冲突了可以下一次刷新再尝试)。

网上对这方面的介绍已经很多,大家可以自行搜索寻找更好的方案(例如Unity 的DOTS Jobsystem)。

public int _datalock = 0;//锁
public Queue<SomeData> dataCache = new Queue<SomeData>();//任务数据缓存队列
/// <summary>
/// 获取数据
/// 由于unity多线程的执行问题,数据将先被缓存,待下一次Update方法执行时进行视图更新。
/// </summary>
/// <param name="dataFrame"></param>
protected void DataReceivingCallBack(SomeData dataFrame)
{
    if (0 == Interlocked.Exchange(ref _datalock, 1))
        {
            dataCache.Enqueue(dataFrame);//数据入列
            Interlocked.Exchange(ref _datalock , 0);
        }
    else 
        //这里可能出现接受数据不成功状况,应回复发送者,要求重新发送
        Debug.LogError("data not received.");
}


protected void Update()
{
    if (0 == Interlocked.Exchange(ref _datalock , 1))
    {
        if (dataCache.Count > 0)
        {
            var dataFrame = dataCache.Dequeue();//数据出列
            //... ... do something
        }
        Interlocked.Exchange(ref _datalock , 0);
    }
    else
    {
        Debug.LogWarning($"[Thread Conflict]...");//这里可以等待下一次Update再处理。
    }
}

在项目里面使用了.NET的Async Socket代码,然后不知道什么时候起出现了这个怪异的报错:

CompareBaseObjectsInternal can only be called from the main thread.
Constructors and field initializers will be executed from the loading thread when loading a scene.
Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.

怪异是因为报错并没有影响程序执行,而是正确地接收了服务器返回的信息,并且通过回调正确显示出来,说明回调被执行了。但是从回调再调用一个解析器却一直不执行。是不是上面的错误导致的?

搜索百度CompareBaseObjectsInternal关键词只有8个结果,Error: CompareBaseObjectsInternal can only be called from the main thread_移动应用的春天-CSDN博客相对有些意义,但是博文为转载,后来看到原文,大致的意思是如果从子线程调用主线程的callback,需要给回调加一个interface。这个和我的情况不太一样,参考意义不大。

然后google,很不幸古大哥一直加载加载。。。于是去yahoo,居然还没有抽筋。最前面搜索出来的几篇文章就是unity论坛上的,大致看出一些眉目。

CompareBaseObjectsInternal error - Unity Forum

CompareBaseObjectsInternal can only be called from the main thread == annoying - Unity ForumCompareBaseObjectsInternal can only be called from the main thread == annoying - Unity Forum

http://community.kii.com/t/error-comparebaseobjectsinternal-can-only-be-called-from-the-main-thread/378#!

CompareBaseObjectsInternal can only be called from the main thread. - Unity Answers

分析出来我的问题大概是在主线程里面注册了回调,

        //注册所有事件的响应代码
        NetEventManager.GetInstance().netMessageReceived_event += OnNetMessageReceived;
        NetEventManager.GetInstance().gateServerConnected_event += OnGateServerConnected;
        NetEventManager.GetInstance().errorMessageOccured_event += OnErrorReceived;

然后socketclient对象是异步的,所以做了多线程处理,当socketclient接收到消息时,回调了主线程的函数(继承自monobehaviour),这时候就导致了报错。上面第一篇unity论坛博文解释得比较清楚,大概就是unity对于API调用主线程做了限制:

“Unity chose to limit API calls to main-thread, to make a simple and solid threading model that everyone can understand and use.”

根据这个解释,我把上面的代码注释掉,程序就不报错了。估计这些事件响应代码要放到非monobehavior的类里面去,就可以避免上面说的线程安全问题。

由于我对多线程编程基本还是小白,上面的解释是否正确、有没有更好的解决,还希望各位指点。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值