调用并行协程并等待它们全部结束
Call parallel coroutines and wait for all of them to be over
我有一些协程:
IEnumerator a(){ /* code */ }
IEnumerator b(){ /* code */ }
IEnumerator c(){ /* code */ }
我想创建一个并行调用 a
、b
和 c
的协程,但要等待它们全部完成才能继续,例如:
IEnumerator d(){
StartCoroutine(a());
StartCoroutine(b());
StartCoroutine(c());
wait until all of them are over
print("all over");
}
显然我可以为每个协程使用布尔值来保存其当前状态,但由于这种方法不可扩展,我更喜欢更直接的解决方案。
当你安装这个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
您可以尝试 Invoke("MethodName",timeinFloat) 并在每个方法中添加一个计数器(int)/一个布尔值。全部完成后运行,根据counter/bool条件,就可以继续执行了。
如果Invoke时间设置为0,则在下一个更新帧周期运行
我用的方法,代码也比较清晰好用:
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");
}
也可以使用协程背后的底层迭代器,自己调用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-
它基本上是一个 ||
运算符,但它将评估您的所有表达式,从而有效地推进每个迭代器,即使另一个迭代器已经完成。
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...");
}
根据 Biswadeep Sarkar 的回答,这是另一个 feature/method 使用协程和 IEnumerators 的方法。
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) 状态的方法。潜在地,您还可以在执行其他方法的过程中开始做其他事情,例如更新进度条以显示您已达到的加载阶段,前提是您适当地更改了您的产量 return。如果您有两种方法试图轮流使用,请注意死锁。
我有一些协程:
IEnumerator a(){ /* code */ }
IEnumerator b(){ /* code */ }
IEnumerator c(){ /* code */ }
我想创建一个并行调用 a
、b
和 c
的协程,但要等待它们全部完成才能继续,例如:
IEnumerator d(){
StartCoroutine(a());
StartCoroutine(b());
StartCoroutine(c());
wait until all of them are over
print("all over");
}
显然我可以为每个协程使用布尔值来保存其当前状态,但由于这种方法不可扩展,我更喜欢更直接的解决方案。
当你安装这个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
您可以尝试 Invoke("MethodName",timeinFloat) 并在每个方法中添加一个计数器(int)/一个布尔值。全部完成后运行,根据counter/bool条件,就可以继续执行了。
如果Invoke时间设置为0,则在下一个更新帧周期运行
我用的方法,代码也比较清晰好用:
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");
}
也可以使用协程背后的底层迭代器,自己调用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-
它基本上是一个 ||
运算符,但它将评估您的所有表达式,从而有效地推进每个迭代器,即使另一个迭代器已经完成。
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...");
}
根据 Biswadeep Sarkar 的回答,这是另一个 feature/method 使用协程和 IEnumerators 的方法。
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) 状态的方法。潜在地,您还可以在执行其他方法的过程中开始做其他事情,例如更新进度条以显示您已达到的加载阶段,前提是您适当地更改了您的产量 return。如果您有两种方法试图轮流使用,请注意死锁。