1

Newer version of C# has async/await. But in Unity there is only yield. How do I implement a method where I can yield in parallel?

Similar to Promise.all([]) in Javascript, we don't care which one finishes first, we only care when they are all done.

To give more context, imagine you are designing a procedural terrain generator that generate in chunks; and you have already setup each chunk to generate using a ThreadPool, and then provide an API that returns an IEnumerator:

IEnumerator GenerateChunk() {
  // procedural generation
  // queue itself onto a ThreadPool
  // when done, yield
}

IEnumerator GenerateChunks() {
  for (int i = 0; i < chunks.Length; i++) {
    yield return chunks[i].GenerateChunk();
  }
}

void GenerateMap() {
  StartCoroutine(GenerateChunks());
}

Can we do something like yield IEnumerator[]?

UPDATE: I am not sure I have expressed myself clearly. Basically I want to kick start all GenerateChunk at once, and allow them to finish as fast as possible, instead of yielding one after another.

Is my code already doing that, or do I need anything else?

bitinn
  • 9,188
  • 10
  • 38
  • 64
  • The correct answer might be `Enumerable.Concat()` http://stackoverflow.com/questions/1270024/nested-yield-return-with-ienumerable – bitinn Sep 22 '16 at 13:15
  • **TL;DR: Ignore all above, my question is [better summarized here](http://forum.unity3d.com/threads/difference-between-yield-return-ienumerator-and-yield-return-startcoroutine.432571/). I apologize for not making this clear at the get-go.** – bitinn Sep 22 '16 at 16:53

2 Answers2

1
yield return StartCoroutine(chunks[i].GenerateChunk());
Nika Kasradze
  • 2,834
  • 3
  • 25
  • 48
1

I read your question, read the answers and the comments you left and I believe that you do not understand how coroutine work.

Coroutine does not run in parallel. It will run your code in order and at-same time, in the same Thread.

UPDATE: I am not sure I have expressed myself clearly. Basically I want to kick start all GenerateChunk at once, and allow them to finish as fast as possible, instead of yielding one after another.

Is my code already doing that, or do I need anything else?

No. If you want to call that function as fast as position, you should not yield after each function. Each time you yield, you are waiting for one frame. Did I even mention that you are not even calling the GenerateChunk function properly? Look closely, you will notice that too. GenerateChunk function is a coroutine function and must be be called with StartCoroutine().

Do not yield after starting GenerateChunk in the for loop. You should only yield or break outside the for loop function. You can run a simple performance test between your code and the code below to verify this with Stopwatch.

IEnumerator GenerateChunk()
{
    // procedural generation
    // queue itself onto a ThreadPool
    // when done, yield
}

IEnumerator GenerateChunks()
{
    for (int i = 0; i < chunks.Length; i++)
    {
        StartCoroutine(GenerateChunk());
    }
    yield break;
    //Or yield return null;
}
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • 1
    Just to clarify, I do know they run in the same thread, and I do know how yield chain works. By parallel I mean `resolve` in non-deterministic order. – bitinn Sep 22 '16 at 14:27
  • Also `GenerateChunk` returns an `IEnumerator`, I can yield it, it will eventually bubble up to the main `StartCoroutine` in `GenerateMap`. I don't think you need to wrap it again. – bitinn Sep 22 '16 at 14:31
  • While I am no expert in Unity coroutine, I think if you don't `yield return StartCoroutine()`, it will execute on its own, ie. even when `GenerateChunks` finishes, some of the `GenerateChunk()` might still be running. – bitinn Sep 22 '16 at 14:40
  • 1
    I read your last two comments several times and don't still understand what you are trying to say..... – Programmer Sep 22 '16 at 15:47
  • see http://forum.unity3d.com/threads/difference-between-yield-return-ienumerator-and-yield-return-startcoroutine.432571/ – bitinn Sep 22 '16 at 16:36