C#优雅的完成多线程回调(await与async)
遇到的问题
我在写一个关于从资源服下载网络资源的代码时遇到一个问题,即我们无法在一个方法块中完成我们想要做的所有逻辑,如请求下载和资源加载完成逻辑。
我们通常的处理方式为:
调用资源下载接口,传递资源加载成功回调方法
当资源下载完成后,再去资源加载回调中去做资源处理。
当我们完成一个需求,需要下载多个资源时,回调方法将会越来越多。我们的代码也将越来越复杂。
将会产生如下的狗屎代码:
我们不能够直接得到文件,只能在此处请求资源下载,然后再将逻辑放到DownloadSuccess中,此时我们的代码的复杂度将会上升,如果文件存在七八上十个,那么对于文件下载完成的处理将非常复杂。如果我们需要拿到上面的文件之后,再去根据资源内容下载其他资源,那么将会出现更多的回调事件的嵌套。
我们能不能在请求网络下载的时候,直接获取到资源呢?
答案当然是不可以,我们虽然不能够同步获取到资源,但是我们想是否能够有别的办法在不阻塞主线程的情况下,使代码能够等待资源下载完成呢?
我发现,在C#的语法中有两个关键字:async与await。
我们可以使用await关键字等待async的代码(它在其他的子线程中)回调成功后,再做剩下的工作。
举例:
从这里看出,我们再Pick1中执行了子线程,调用者PickData直接将结果返回给了上级,上级的GetData1有一个await的操作。那么现在这个代码将会以什么顺序执行呢?
我们可以看到,只有在wait的后的代码才会在子线程中执行。
那么我们将代码改进之后。可以得到类似如下的代码
最后,我们发现线程ID,在执行子线程休息之后的代码都改变了当前线程的ID的。
那么在我们实际开发中,是不是就不能使用这个方式对UI进行更新操作呢?(一般子线程中是不允许对UI进行操作的,包括Unity3d中,也不允许对继承MonoBehaviour的类进行操作)
当时我想这个代码大概率会报错,不允许在子线程操作,如:
UnityException: get_isActiveAndEnabled can only be called from the main thread.
但实际上并没有发生这个报错,最后我们看一下在unity中的结果如何:
子线程运行确实是走到了子线程中,但是等到子线程运行结束的时候代码又回到的主线程。
这部分的原理我们通过调试发现应该是在Unity内部有进行一层封装,将消息传递回主线程中
在控制台中的程序堆栈为
具体的关于其原理我没有找到比较深入的资料。有兴趣的同学可以自行去网上学习~