Unity3D协程和lambda回调:内存泄漏安全

3
在协程中调用声明的 System.Action 是安全的吗?
IEnumerator MyCoroutine(){
     bool myBool = true;
     System.Action action = ()=>{myBool=false;}

     StartCoroutine (MyOtherCoroutine(action));//just launch and forget about it
 }

 IEnumerator MyOtherCoroutine(System.Action dangerous_callback){
     yield return null;
     yield return null;//skip several frames
     yield return null;
     dangerous_callback.Invoke();//will cause problems? The owner coroutine (and it's myBool) is no longer on stack
 }

我的担忧是关于在第一个协程中分配的`myBool`。`MyCoroutine`不会等待`MyOtherCoroutine`完成。我可以看到`System.Action`不会被回收,因为它确实仍然被`MyOtherCoroutine`引用,但是`MyCoroutine`(其布尔变量由该Action修改)应该已经被释放了吗?

3
你是否遇到了问题,还是在问是否可以这样做? - Programmer
1
我认为在协程中使用回调很方便,因为无法使用'ref'或'out'参数。但这似乎可能会导致内存泄漏,后期可能难以捕获?只是想问一下是否可以使用它。 - Kari
1
如果你想知道为什么这是安全的原因/机制,可以搜索“委托闭包捕获”来了解。 - Jay
2个回答

4
但这似乎是一种可能的内存泄漏,后面很难发现?只是想问一下是否可以使用它。 没有内存泄漏。如果需要在协程函数中使用回调函数,完全可以这样做。请记住,这是C#,在C#中比在C ++中创建内存泄漏更加困难,除非您误用了Unity的API,这可能会在本地(C ++)端创建内存泄漏,但这里不是这种情况。
尽管如此,在调用之前应该检查Action是否为空,否则可能会出现代码问题。
if (dangerous_callback != null)
    dangerous_callback.Invoke();

谢谢!这真的很奇怪,因为myBool直接属于MyCoroutine,我会认为Action会尝试访问一些已释放的内存。我一直把它们看作是在达到}时从执行堆栈中删除的函数。所以在c#中,这个原始协程会一直保持,直到我的回调被调用? - Kari
Action是一个delegate,它是一种引用类型。当你将它传递给另一个协程函数时,GC会允许它存在,直到该协程函数存在为止。如果它在任何地方有引用,它将永远不会被释放。当MyOtherCoroutine执行完成后,它没有任何引用,因此现在可以被GC收集。 - Programmer
抱歉我这么固执:D,但是在第一个协程中分配的myBool怎么办?MyCoroutine不会等到MyOtherCoroutine完成。我可以看到System.Action不会被收集,因为它确实仍然被MyOtherCoroutine引用,但是MyCoroutine(其布尔变量由Action修改)应该已经被释放了吧? - Kari
3
没问题。如果你从C++转到使用C#,会有很多疑问。*"我的Coroutine(布尔类型变量被Action修改),应该已经被释放了吗?"* 不是的。由于它在Action引用类型或action变量中被引用,编译器将把myBool变量移动到一个辅助类中。请参见文章。由于你使用了lambda表达式,它会创建一个闭包。 - Programmer
我想补充一点,你可以使用空值传播运算符在一行中编写它:dangerous_callback?.Invoke(); - Yes Barry

0

很快你就可以使用:

dangerous_callback?.Invoke();

问题是关于dangerous_callback打算使用的变量,而不是关于回调函数本身。 - Kari

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接