This has been driving me crazy. If anyone has insight in why it’s behaving like this, I’d love to hear it.
So, I have a while loop inside a coroutine. It works fine. However, yielding each frame is too slow for my purposes. So I’m trying to make it yield after every 4 loops. In my test case I want the while to execute a total of 1000 times (yielding 250 times along the way). It always freezes unity after around 400 total loops. Help?
private void CoroutineFunction()
{
int loop = 0;
//start = 0; end = 1000;
while (start != end)
{
start++;
//some instantiation code omited here
if (loop % 4 == 0)
{
yield return null;
}
loop++;
}
}
To be clear - if I remove the loop
variable and the if %
check, and just yield every frame it works. But why would my changes make unity freeze? How would they cause the while loop to get stuck? How else could I implement yielding every x loops?
It might depend on your “some instantiation code omited here”. The code provided should work fine. Maybe each instantiated object also starts this or a similar coroutine? Since there’s no yield between the start and you instantiate code it runs synchronously. Though if you have an infinite recursion you should get a stack overflow and not a freeze (though if the instantiate code is quite heavy it might take a while).
If you just want a certain call count per second you could use a custom fixed update callback. This works similar to Unity’s FixedUpdate but you can specify any desired call rate, even 10000 times per sec. Though it does not perform load balancing. So you should ensure the cost of a single call is not higher than the deltatime between two fixed calls. So when doing 10000 calls each call should not take longer than 0.0001 seconds (0.1ms). Keep in mind that at a visual framerate of 60 fps there would be about 166 calls each frame.
The CustomFixedUpdate does have a “MaxAllowedTimeStep” (like Unity has) to not get caught in a death spiral.
So the issue was related to the omitted Instantiation code. In hindsight the problem is obvious.
The objects I was instantiating were running some internal code as part of the coroutine that instantiated them. And several of them had while() loops of their own, that in my situation had become infinite loops. The reason was that they did not get properly initialized as their Start() functions were not called until next frame (by which time it was already too late).
The solution was simply to use Awake() instead of Start() on all objects instantiated on the coroutine. This ensures they are properly initialized, even if the next frame hasn’t started yet.