调用并行协程并等待所有协程结束

时间:2019-12-15 22:13:26

标签: c# unity3d coroutine ienumerator

我有一些协程:

IEnumerator a(){ /* code */ }
IEnumerator b(){ /* code */ }
IEnumerator c(){ /* code */ }

我想创建一个协程,它们并行调用abc,但是要等到它们全部完成后再进行操作,例如:

IEnumerator d(){
    StartCoroutine(a());
    StartCoroutine(b());
    StartCoroutine(c());
    wait until all of them are over
    print("all over");
}

显然,我可以为每个协程使用一个布尔值来保存其当前状态,但是由于这种方法不可扩展,因此我希望使用一种更简单的解决方案。

5 个答案:

答案 0 :(得分:2)

我使用的方法,它也是一个清晰易懂的代码:

IEnumerator First() { yield return new WaitForSeconds(1f); }
IEnumerator Second() { yield return new WaitForSeconds(2f); }
IEnumerator Third() { yield return new WaitForSeconds(3f); }

IEnumerator d()
{
    Coroutine a = StartCoroutine(First());
    Coroutine b = StartCoroutine(Second());
    Coroutine c = StartCoroutine(Third());

    //wait until all of them are over
    yield return a;
    yield return b;
    yield return c;

    print("all over");
}

答案 1 :(得分:1)

安装此package (source)时,可以将其实现为异步协程混合方法:

using System.Collections;
using System.Threading.Tasks;
using UnityEngine;

public class TestCoroutines : MonoBehaviour
{

    void Start () => D();

    IEnumerator A () { yield return new WaitForSeconds(1f); print($"A completed in {Time.time}s"); }
    IEnumerator B () { yield return new WaitForSeconds(2f); print($"B completed in {Time.time}s"); }
    IEnumerator C () { yield return new WaitForSeconds(3f); print($"C completed in {Time.time}s"); }

    async void D ()
    {
        Task a = Task.Run( async ()=> await A() );
        Task b = Task.Run( async ()=> await B() );
        Task c = Task.Run( async ()=> await C() );

        await Task.WhenAll( a , b , c );

        print($"D completed in {Time.time}s");
    }

}

控制台输出:

A completed in 1.006965s
B completed in 2.024616s
C completed in 3.003201s
D completed in 3.003201s

答案 2 :(得分:1)

有一个使用标准Unity库的解决方案。

您可以创建协程,以包装要调用的协程,并调用完成时设置标志的闭包。然后,您可以使用WaitUntil等到它们全部成立:

IEnumerator a() {yield new WaitForSeconds(5f);}
IEnumerator b() {yield new WaitForSeconds(3f);}
IEnumerator c() {yield new WaitForSeconds(4f);}

IEnumerator aWrap(Action onDone){yield return a(); onDone();}
IEnumerator bWrap(Action onDone){yield return a(); onDone();}
IEnumerator cWrap(Action onDone){yield return c(); onDone();}

IEnumerator d(){
    int finishedCount = 0;
    Action onDone = new Action(()=>{finishedCount++;});

    StartCoroutine(aWrap(onDone));
    StartCoroutine(bWrap(onDone));
    StartCoroutine(cWrap(onDone));

    yield return new WaitUntil(()=>{finishedCount >= 3});

    print("all over");
}

答案 3 :(得分:1)

您还可以使用协程背后的底层迭代器并自己调用MoveNext

在您的示例中,它将类似于

IEnumerator a(){ /* code */ }
IEnumerator b(){ /* code */ }
IEnumerator c(){ /* code */ }

IEnumerator d(){
    IEnumerator iea = a();
    IEnumerator ieb = b();
    IEnumerator iec = c();
    // Note the single | operator is intended here
    while (iea.MoveNext() | ieb.MoveNext() | iec.MoveNext()) {
        yield return null;
    }
    print("all over");
}

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/boolean-logical-operators#logical-or-operator-处查看有关|运算符的文档
它基本上是一个||运算符,但是它将评估您的所有表达式,从而即使每个迭代器都已经完成,也可以有效地推进每个迭代器。

答案 4 :(得分:0)

您可以尝试Invoke(“ MethodName”,timeinFloat)并在每个方法中添加一个counter(int)/ bool。当它们全部完成运行后,根据计数器/布尔条件,您可以继续执行。

如果Invoke时间设置为0,它将在下一个更新帧周期中运行