- Home /
Do recursive coroutines leak memory?
I've got a function that needs to run roughly every half second, so naturally I'm looking at a coroutine.
I'm just curious, is this sufficient, or will this cause the stack frame associated with this coroutine to get bigger and bigger over time?
void Start ()
{
StartCoroutine(LookForSquidward());
}
IEnumerator LookForSquidward()
{
yield return new WaitForSeconds(0.5f);
if (IsThisPatrick()) {
Debug.Log("Yes, this is Squidward.");
} else {
Debug.Log ("No, this is Patrick.");
}
StartCoroutine(LookForSquidward());
}
I know the approach of using a while loop instead of a recursive StartCoroutine
would unambiguously not grow the stack frame, but I'd like to know the specifics of how this approach works (or doesn't).
Relatively unrelated to your question, ins$$anonymous$$d of constantly polling/querying whether it's Patrick or Squidward, it might be better, more elegant, and more to the point if the event that replaces Patrick with Squidward and vice versa would directly call a callback function such as NowItsSquidward() and NowItsPatrick() respectively. This could also be implemented via EventHandlers.
Well, yes, haha, that would be more efficient. This is a contrived example optimized for humor rather than an ideal implementation of Squid/Patrick detection.
Answer by Kryptos · Jun 12, 2012 at 03:53 PM
This way this will work because the coroutine is not stricly recursive: each coroutine overlaps the next one for one frame. To be recursive, and hence have possible issues, you would need to do that instead:
// FAULTY CODE. DON'T DO THIS.
void Start ()
{
StartCoroutine(LookForSquidward());
}
IEnumerator LookForSquidward()
{
yield return new WaitForSeconds(0.5f);
if (IsThisPatrick()) {
Debug.Log("Yes, this is Squidward.");
} else {
Debug.Log ("No, this is Patrick.");
}
yield return StartCoroutine(LookForSquidward());
}
But IMHO, using loops is a better approach. Still, your code works and doesn't raise any recursive issue.
It's generally not good to post negative examples ;) I've done so as well, but you should mark it as faulty code. Something like:
// DON'T USE THIS
Thanks for answering, that makes sense, good to know that coroutines don't cause the stack to grow unnecessarily. Is there a specific reason why loops are preferred in general? It makes sense that algorithms that would read better recursively are implemented iteratively for game development (ie: recursive DFS of the hierarchy would be wasteful). But in this case it seems like the amount of memory allocated is about the same. Is this correct? If so, why are loops preferred over recursive coroutines in cases like this?
It's not just the allocation (although I realise your question is mainly concerned about memory usage), it's the complete setup and handling of a coroutine each time. Although I'm not sure how coroutines are exactly handled internally, it will involve stack access, additional function calls, probably managing the internal list of active coroutines, and so on.
Plus you'll also have to do the yield new WaitForSeconds anyway.
Got it, that answers my question. Personally, I was pursuing this because I have a need for a repeated action that will happen at a different random interval each time, so the static interval of InvokeRepeating doesn't do the job. I hadn't thought of using a loop IN a coroutine. It seems, then, a standard template for repeating a function at a dynamic interval would be:
public void Start() {
StartCoroutine("Loop");
}
private void Loop() {
while (true) {
// Do something here
yield return new WaitForSeconds(someFloatThatGetsChangedNowAndThen);
// Alternatively, or in addition, do something here
}
}
@$$anonymous$$ax-Pixel
That's the right idea. The clearest and most efficient approach in these cases is a loop within the coroutine. I was asking about using recursive coroutines out of curiosity and because of a weird case that ended up being clearer with a loop anyway.
$$anonymous$$ake sure your Loop function returns an IEnumerator, and if you don't expect this script to get destroyed remember to give yourself an exit condition (whether in the while statement or via a break somewhere in the loop body) so you can end your coroutine.
Your answer
Follow this Question
Related Questions
Coroutines and states 1 Answer
Brain exploding ... Problem with an internal Coroutine manager 3 Answers
Can't Stop couroutines 1 Answer
Dynamically updating a Coroutine's parameters every frame 1 Answer
Why doesnt my coroutine ever end? 2 Answers