【问题标题】:Call parallel coroutines and wait for all of them to be over调用并行协程并等待它们全部结束
【发布时间】:2020-04-08 10:30:34
【问题描述】:

我有一些协程:

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");
}

显然我可以为每个协程使用布尔值来保存其当前状态,但由于这种方法不可扩展,我更喜欢更直接的解决方案。

【问题讨论】:

  • 这能回答你的问题吗? Wait for coroutine to finish
  • 没有。我知道yield StartCoroutine(a()) 等待协程完成,但我想同时启动它们,并行执行。每个都有自己的持续时间,当最后一个结束时,d 应该打印“all over”。
  • 协程本质上是单线程的。从本质上讲,它们不能“并行”运行。你需要线程
  • @AndrewŁukasik 你说得对。但认为它们“并行执行”是错误的。无论如何,等待协程完成的唯一方法是让调用方法成为协程(或编写自己的标志系统)。
  • 在 Daniel 使用的抽象级别上,它们并行运行。这就像通过告诉他们相对论来回答某人关于使用牛顿第二定律的问题。

标签: c# unity3d coroutine ienumerator


【解决方案1】:

我用的方法,代码也有点清晰,好用:

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");
}

【讨论】:

  • @jrmgx 协程将并行运行。 “All over”将在 3 秒后打印出来。
  • 答案是正确的,将并行运行。如果是这样,将按顺序运行: return yield StartCoroutine(a()); @BiswadeepSarkar 您忘记了协程中的 return 语句,并且变量和函数名称发生冲突。也许你想解决这个问题,让人们更容易复制/粘贴和测试它。干杯。
  • @BiswadeepSarkar 你确实是对的,注意return yield new xxx 的语法是错误的,应该是yield return xxx。除了我很高兴今天学到了新东西:)
  • @jrmgx 很高兴,你喜欢我的回答!我已经更新了答案。
【解决方案2】:

你也可以使用协程背后的底层迭代器,自己调用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-
它基本上是一个 || 运算符,但它会评估你的所有表达式,从而有效地推进每个迭代器,即使另一个迭代器已经完成。

【讨论】:

    【解决方案3】:

    当你安装这个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
    

    【讨论】:

    • 调用await A()时不需要使用StartCoroutine吗?
    • 上面的代码是我写的,所以没有。但是这个包需要这样才能以这种方式工作,因为它公开了正确的扩展方法
    【解决方案4】:

    Biswadeep Sarkar 的回答非常好。我改进了一点。做了一个等待并行协程的通用函数。随意使用并进一步修改它。

      IEnumerator WaitForSomeCoroutines(params IEnumerator[] ienumerators)
        {
            Debug.Log($"Start time of parallel routines: {Time.time}");
            if (ienumerators != null & ienumerators.Length > 0)
            {
                Coroutine[] coroutines = new Coroutine[ienumerators.Length];
                for (int i = 0; i < ienumerators.Length; i++)
                    coroutines[i] = StartCoroutine(ienumerators[i]);
                for (int i = 0; i < coroutines.Length; i++)
                    yield return coroutines[i];
            }
            else 
                yield return null;
            Debug.Log($"End time of parallel routines: {Time.time}");
        }
    

    所以,假设你有一些例程:

     IEnumerator A(float time) { yield return new WaitForSeconds(time); }
     IEnumerator B(float time) { yield return new WaitForSeconds(time); }
     IEnumerator C(float time) { yield return new WaitForSeconds(time); }    
      
    

    您希望例程 D 等待例程 A、B 和 C 完成。您这样做:

      IEnumerator D()
            {
                // We wait until 3 other coroutines are finished.
               yield return StartCoroutine(WaitForSomeCoroutines(
                    A(1f),
                    B(3f),
                    C(2f)));
        
               // Now we make our stuff.
               Debug.Log("Working on my stuff...");
            }
    

    【讨论】:

      【解决方案5】:

      根据 Biswadeep Sarkar 的回答,这是使用协程和 IEnumerator 的另一个功能/方法。

      IEnumerator CountTill(int numberToCountTill) {
          for (int i = 0; i < numberToCountTill; i++) {
              yield return i;
          }
          yield return "Done";
      }
      IEnumerator a;
      IEnumerator b;
      IEnumerator c;
      
      IEnumerator d()
      {
          a = CountTill(1);
          b = CountTill(3);
          c = CountTill(9);
      
          StartCoroutine(a);
          StartCoroutine(b);
          StartCoroutine(c);
      
          //wait until all of them are over
          while (a.Current.ToString() != "Done" && 
                 b.Current.ToString() != "Done" && 
                 c.Current.ToString() != "Done")
          {
              yield return "Waiting";
          }
      
      
          print("all over");
      }
      

      这是您可以通过保持对函数的引用并定期检查值来检查协程(IEnumerator)状态的方法。潜在地,您还可以在其他方法的执行过程中开始做其他事情,例如更新进度条以显示您已达到的加载阶段,前提是您适当地更改了您的收益回报。不过,如果您有两种方法尝试轮流使用,请注意死锁。

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

          猜你喜欢
          • 2021-06-10
          • 1970-01-01
          • 2022-12-18
          • 1970-01-01
          • 2019-05-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多