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

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

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

再看我们使用Socket中的TCP通讯,当 服务器 发送数据过来后,上图中左下角的输入日志已经接收到了服务器发送的88878的数据,但是屏幕中的Text组件并没有更新显示。
先说明上面所有的网络请求后,返回更新显示Text的值都是用的同一个Action的委托方法
/// <summary>
/// 写返回Action的处理方法
/// </summary>
private void InitAction()
actionRes = new Action<bool, string>((bl, str) =>
Debug.Log(str);
if (bl)
txtshow.text = str;
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 + "\r\n");
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的方法,看看效果怎么样。