笔记|Unity异步处理与UI Text显示的问题

2020-09-18 15:42:15 浏览数 (1)

前言

这阵子一有空就在研究Unity3D网络通讯,使用过程中访问通过协程的方式收到返回的数据直接更新Text的显示值都没有问题,结果在处理Socket通讯TCP方式采用异步时遇到了问题,本章主要就是记录一下测试的过程和处理方法,关于Unity3D与后台的网络通讯这块后面会有一个系列发出来。

遇到的问题

上图中可以看到,我们首先调用的是Restful正常的Get,Post的方法,获取到的数据在屏幕上Text的组件中也正常显示了。

再看我们使用Socket中的TCP通讯,当服务器发送数据过来后,上图中左下角的输入日志已经接收到了服务器发送的88878的数据,但是屏幕中的Text组件并没有更新显示。

先说明上面所有的网络请求后,返回更新显示Text的值都是用的同一个Action的委托方法

代码语言:javascript复制
 /// <summary>
    /// 写返回Action的处理方法
    /// </summary>
    private void InitAction()
    {
        actionRes = new Action<bool, string>((bl, str) =>
        {
            Debug.Log(str);
            if (bl)
            {
                txtshow.text = str;
            }
            else
            {
                string resjson = "{"array":"   str   "}";
                _showstr = resjson;
                WeatherData lists = JsonUtility.FromJson<WeatherData>(resjson);
                StringBuilder sb = new StringBuilder();
                foreach (WeatherForecast item in lists.array)
                {
                    sb.Append("Date:"   item.Date   " Summary:"   item.Summary   " TemperatureF:"
                          item.TemperatureF   "TemperatureC:"   item.TemperatureC   "rn");
                }
                txtshow.text = sb.ToString();
            }
        });
    }

问题排查

微卡智享

即然我们回调方法都一样,那我们就看看WebApi和Socket的通讯有什么不一样。

WebApi调用

上图中可以看到我们访问HttpRestful的Get方法里面是用协程的操作完成的,当请求返回数据后,直接调用action后就是我们前面代码的回调函数更新显示,接下来我们再看看Socket TCP的通讯。

Socket TCP通讯

上图中,我们使用Socket的TCP接收时,首先定义了一个TransData的类,把action传入进去,然后通过NetworkStream的BeginRead的方法进行处理数据接收。

TransData的类结构

上面几个图就是BeginRead中加入的回调函数,在接收完后我们直接调用transData类中的actionResult方法做后续的处理。

问题思考

不说两个方法接收数据后的处理,这里肯定都是一样的,最终都是把接收到的返回结果调用Action回调方法中执行,那问题会出来哪呢?

仔细再看了一个,在Restful的请求里面,我们用的是协程的方式处理的,而在Socket Tcp中,我们的BeginRead是一个异步的线程处理的,搜索了一下Unity中的协程解析,有这第一段说:

协程的作用一共有两点:1)延时(等待)一段时间执行代码;2)等某个操作完成之后再执行后面的代码。总结起来就是一句话:控制代码在特定的时机执行。

很多初学者,都会下意识地觉得协程是异步执行的,都会觉得协程是C# 线程的替代品,是Unity不使用线程的解决方案。

所以首先,请你牢记:协程不是线程,也不是异步执行的。协程和 MonoBehaviour 的 Update函数一样也是在MainThread中执行的。使用协程你不用考虑同步和锁的问题。

从上面这段话来说,协程不是异步执行的,所以text更新可以直接显示,而使用BeginRead时是异步线程操作的,做过多线程开发的同学应该都处理过线程与UI进行同步的问题(Andorid的开发可能更多),接下来我们就直接做个验证看看。

测试验证

我在Tcp通讯的Recv方法里面,使用BeginRead异步处理之前,先调用一下Action的方法,看看效果怎么样。

从上图中可以看到,在进入BeginRead之前,我们直接调用action的方法后,Text也是直接显示出来的没有问题了,这就验证了上面所说的问题,所以我们下一步就考虑怎么处理线程和UI同步的问题即可。

解决办法

微卡智享

其实找到问题后,解决这个的方法也更简单了,因为Unity中本身就有Update(),OnGUI()等方法,在每帧执行,所以我们可以直接把返回的数据做为一个内部变量,然后判断这个变量是否修改了,修改后再在相关的方法中更新Text即可。

01

加入更新显示变量

增加两个变量,一个是返回值保存到_showstr中,另一个是bool类型的,每当_showstr改变时,更改这个改变的值。

02

修改Action的赋值

修改Action的方法,把原来的txtshow.text赋值屏蔽后,改为返回的字符串赋值给_showstr,并且把_isshowstrupd的值改为True,用于记录当前显示值已经更新。

03

OnGUI中更新显示

然后在OnGUI方法中,判断如果_isshowstrupd为true时,修改txtshow.text的赋值更新,再把_isshowstrupd=false;

通过上面这几步就解决Text的显示问题了。

实现效果

0 人点赞