unity3D中使用协程来做“多次调用一次更新”特性的一个大坑。

有些事情一帧之内只需要做一次,比如收发邮件时,一帧内数据层多次的变化,表现层只需要一次更新。
我采用协程做这个事情,为了避免发起多个协程,我记录了一个协程是否发起的变量。
但是u3d里的coroutine,在发起的脚本acvited=false后,就不更新了,并且我测试过,再恢复成actived = true,协程依旧不更新了,就像没调用一样。

以下是u3d关于协程的调用机制的解释:

“在Unity3D中,使用MonoBehaviour.StartCoroutine方法即可开启一个协同程序,也就是说该方法必须在MonoBehaviour或继承于MonoBehaviour的类中调用。

       在Unity3D中,使用StartCoroutine(string methodName)和StartCoroutine(IEnumerator routine)都可以开启一个线程。区别在于使用字符串作为参数可以开启线程并在线程结束前终止线程,相反使用IEnumerator 作为参数只能等待线程的结束而不能随时终止(除非使用StopAllCoroutines()方法);另外使用字符串作为参数时,开启线程时最多只能传递一个参数,并且性能消耗会更大一点,而使用IEnumerator 作为参数则没有这个限制。

        在Unity3D中,使用StopCoroutine(string methodName)来终止一个协同程序,使用StopAllCoroutines()来终止所有可以终止的协同程序,但这两个方法都只能终止该MonoBehaviour中的协同程序。

        还有一种方法可以终止协同程序,即将协同程序所在gameobject的active属性设置为false,当再次设置active为ture时,协同程序并不会再开启;如是将协同程序所在脚本的enabled设置为false则不会生效。这是因为协同程序被开启后作为一个线程在运行,而MonoBehaviour也是一个线程,他们成为互不干扰的模块,除非代码中用调用,他们共同作用于同一个对象,只有当对象不可见才能同时终止这两个线程。然而,为了管理我们额外开启的线程,Unity3D将协同程序的调用放在了MonoBehaviour中,这样我们在编程时就可以方便的调用指定脚本中的协同程序,而不是无法去管理,特别是对于只根据方法名来判断线程的方式在多人开发中很容易出错,这样的设计保证了对象、脚本的条理化管理,并防止了重名。”


而我那些标志还是【已经发起】状态,这样就发生死锁了----想更新的发起不了,之前的异常结束导致标志复位不了。

上述现象发生在用户收到邮件后的一帧里操作了鼠标关闭了邮件系统。
由于网络消息分派比用户输入的时机靠前(我们项目里的既有结构),导致协程发起了,但实际无法执行。
如果先关闭,再处理数据,就会走关闭时的处理,就不会出错了---可是网络消息也是有可能导致UI关闭的,看业务逻辑了。

回到现状,
我只能在隐藏的时候,恢复标志。
可是u3d隐藏对象过于随意,大多使用GameObject.SetActiveRecursively(bool)接口干活。
这导致我无法保证所有处理该对象的代码都知道恢复标志这个事情。
我可以提炼一个特殊的Visible接口,确保标志恢复,但是会有以下情况:
如果将逻辑分散到多个脚本组件,如何访问到这个特殊的Visible接口就会变得很麻烦。
如果这样,那我还不如用Update来做。

目前的解决方案是:
一个场合恰好只有一个脚本,能满足“提炼一个特殊的Visible接口,确保标志恢复”。
另外一个场合是很多脚本,刚好协程只做可见性控制,这导致出错了也不怕,因为会被其他可见性操作恢复。

 

谁能给出基于Coroutine的不会出死锁问题的更新方案?

今天在好友列表的更新也用了协程。而且关闭逻辑刚好在别的脚本里,现在的打算是让关闭按钮捕获用户输入后,转给Model层,再分别通知各个脚本组件执行恢复操作。

希望这么做了,效率会比update效率高,反正我觉得这不是核心性能瓶颈。

 

zpbaaa 的方式正解~~多谢了。

在OnDisable的时候恢复即可,不用中转了。

 

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值