【问题标题】:Unity using coroutine like a thread?Unity 像线程一样使用协程?
【发布时间】:2018-08-26 12:39:48
【问题描述】:

我有一个耗时的功能,比如...

Debug.Log("A");

void Froze_Function() {
  for(int i=0; i<50000; ++i)
    // take some time...

  Debug.Log("B");
}

Debug.Log("C");

我不希望“Froze_Function”延迟我的统一程序,所以我希望它在其他线程中运行。

Unity有协程,但是不知道怎么用协程正确实现这个功能。

请不要说优化我的功能...

【问题讨论】:

  • 答案真的取决于你在做什么,这很耗时。您已经发布了耗时的代码部分。
  • 您应该通过某些条件将 Froze_Function 拆分为多个部分(迭代?),然后 yield return new WaitForEndOfFrame()。每次迭代后,您的函数将停止并等待下一帧执行下一步。非常粗鲁的例子,但这就是它的工作原理。顺便说一句,它不是线程,不能用作线程。只能在帧之间(或其他条件)将作业分解成碎片。
  • @AndrewSnejovski 哇.. 太糟糕了。那么我的答案会将 50000 次循环分成 500 或 1000 次......这导致需要 100 或 50 帧来计算?
  • 您可以在一帧中进行 100 次迭代,并记住在该帧上花费的时间。在下一次迭代中,您可以增加每帧的操作数,直到帧速率超过某个限制,例如 60fps。
  • @AndrewSnejovski 这可能是一个答案!虽然 w 必须记住花费在 100 次迭代上的时间......或任何迭代才能知道每帧的操作计数

标签: unity3d coroutine


【解决方案1】:
void Start()
{
    Debug.Log("A");
    StartCoroutine(Froze_Function());
    Debug.Log("C");
}

//Inside a method (class should be MonoBehavour) call: StartCoroutine(Froze_Function()); 

IEnumerator Froze_Function()
{
    float dist;
    for (int i = 0; i < 500; ++i)
    {
        dist = Vector3.Distance(Vector3.up, Vector3.left);
        yield return 0;
    }

        Debug.Log("B");
    yield return 0;
}

【讨论】:

  • 我编辑了我的代码。我认为您的解决方案可能会导致控制台写入 A B C。不是我想要的 A C B。
  • 好的,我编辑了它。现在它应该可以按您的意愿工作了。我用向量距离作为一个耗时任务的例子。
  • hmm.. 虽然结果可以 ACB,但我认为它必须需要 500 帧才能得到答案。 (或我的代码中的 50000 帧)
  • 这段代码的工作方式如下:打印 A -> 启动协程(它在后台继续工作,没有冻结并且已经在计算) -> 打印 C -> 协程工作直到完成 -> 打印 B。是的它需要很多帧,但不会停止正在运行的程序。
【解决方案2】:

您只是将其用作耗时任务的示例,还是您实际上需要等待设定的时间?如果您确实需要等待设定的时间,则 IENumerator(协程)中的 yield return new WaitForSeconds(n) 将等待 n 秒。 Docs on WaitforSeconds

多线程方法

也就是说,您也可以为此使用新线程,具体取决于您想要做什么。在统一处理多个线程时,请记住,您将能够从新线程访问单一行为。这意味着诸如gameObject.transform.position 之类的东西将无法访问。 Debug.Log 是一个例外,可以被访问。如果您想编辑游戏对象,请使用协程。

像这样启动一个新线程本身很容易,这样做的一个优点是,如果你想等待设定的时间而不损失任何性能,你可以使用 Thread.Sleep ,而在协程上你仍然会失去任何性能:

using System.Threading;
public class ThreadStarter
{
   Start()
   {
      Debug.Log("a");
      Thread newThread = new Thread(() => FunctionRunningOnThread());//this function will now execute on the new thread
      newThread.Start();
      Debug.Log("c");
   }

   private void FunctionRunningOnThread()
   {
       Thread.Sleep(5000)//Thread.Sleep is in milliseconds, so this waits 5 seconds
       Debug.Log("B"); //debug.log 
   }
}

这将在 5 秒后打印“a c”,然后打印“b”。请记住,使用多个线程安全工作比使用单个线程更难,因为您必须处理竞争条件,锁定变量,这样您就不会尝试从多个位置编辑它们同时还有更多的东西。除非您知道自己在做什么,否则不鼓励这样做,并且使用协程是更安全的选择。

启动协程 协程函数总是需要是IENumerator 类型,并且使用StartCoroutine 函数调用并且总是使用yield return 返回一个值,这个值可以是null

Start()
{
    Debug.Log("a");
    StartCoroutine(SomeFunction());
    Debug.Log("c";
}

private IENumerator SomeFunction()
{
   yield return new WaitForSeconds(5);//this will wait 5 seconds
   Debug.Log("b");

   //Alternatively you can use this if you want to wait a set of frames
   for(int i = 0; i < 50000; i++)//this will wait 50000 frames
   {
       i++
       yield return new WaitForEndOfFrame();/
   }
}

这也会在 5 秒后打印“a”“c”和“b”。这样做的好处是你仍然可以在协程中编辑游戏对象,而且你不必担心线程安全。

【讨论】:

  • 感谢您的回答。很抱歉让您感到困惑。我真正想做的不是等待5秒或5000帧直到我的函数完成,而只是让它及时完成。(不在主线程中,因为它会延迟我的程序)
  • 不用担心,您仍然可以使用上面的示例。在 newThread 或 someFunction IENumerator(协程)上运行的 FunctionRunningOnThread 函数内运行的任何代码对于主线程都是非阻塞的(尽管从技术上讲协程仍然会阻塞很短的时间,但这是一个很好的解决方案大多数时候)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-28
  • 2019-10-27
  • 2017-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多