首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >单位StartCoroutine

单位StartCoroutine
EN

Stack Overflow用户
提问于 2021-12-31 02:50:07
回答 3查看 3.1K关注 0票数 0

最近,我不得不设计一个计时器系统来周期性地生成球形对象。这是密码。如果你分配球的obj,产卵项,那么它可能会起作用。

代码语言:javascript
复制
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
using System;
using System.Collections.Generic;

public class BallCup: MonoBehaviour
{
    bool wait = false;
    public float respawnTerm = 3;
    public GameObject Ball;
    public Vector3 spawnpoint;
    // Start is called before the first frame update
    
    void Start()
    {
        spawnpoint = transform.position - new Vector3(0, 0, 10);
    }

    // Update is called once per frame
    void Update()
    {
        createBallRegularly();

    }

    void createBallRegularly()
    {
        if (!wait)
        {
            StartCoroutine("startTimer");
        }

        else
        { 
            return;
        }
    }

    
    IEnumerator startTimer()
    {
        wait = true;
        //cs entered
        Instantiate(Ball, spawnpoint, Quaternion.identity);
        yield return new WaitForSeconds(respawnTerm);
        //cs ended
        wait = false;

    }

}

我读过一些关于定时器和同步问题的文章。解决同步问题有两种方法。一种方法是投票,我不记得它叫什么,但它是睡觉和醒来。

在UnityManual中,StartCoroutine是创建一个新的工作线程(任务),以独立运行到主任务。

问题是上面的代码,我每次调用该方法来检查临界部分是否空闲。

  1. 是那种投票吗?
  2. 如果我不想每次调用createBallRegularly()的话。有什么方法可以像java线程那样实现吗?
  3. 我听说这个任务与线程类似,那么使用'startcoroutine‘以获得更好的性能会更好吗?

我读过这个。但是在我的计时器的代码和实现之间有一些差距。如何为C#编写线程安全的Unity3D代码?

我也查过这个。https://web.mit.edu/6.005/www/fa15/classes/23-locks/

EN

回答 3

Stack Overflow用户

发布于 2021-12-31 13:50:53

首先,存在一个相当大的误解:柯鲁丁基本上就像一个小的临时Update方法,底层IEnumerator的每个MoveNext步骤都是通过underlying线程中的消息执行的。=> 这绝对与多线程无关!

StartCoroutine是创建一个新的工作线程(任务),以便独立运行到主任务。

我不知道你读过哪本手册,但不,这完全是胡说八道!

来自实际手册

在联合中,协同线是一种方法,它可以暂停执行并将控制返回给Unity,但在下面的帧上继续执行。

尤其是

但是,重要的是要记住,协同线不是线程。在协同线中运行的同步操作仍然在主线程上执行。

当您调用StartCoroutine时会发生什么(简单地说--我也无法访问底层源代码)

  • IEnumerator在此组件上注册为运行的Coroutine。
  • 它还会立即执行所有操作,直到第一个yield语句
  • 那么这取决于yielded 到底是什么?
    • yield return null;基本上意味着只产生一次,然后在下一个Update调用之后继续下一个帧中的其余代码。
    • yield return new WaitForSeconds (3); afaik基本上检查帧一次,时间是否已经过去,然后在下一次Update调用完成后继续进行。
    • 然后有一些特殊的YieldInstruction,比如WaitForEndOfFrame,它基本上在一个特殊消息事件调用(EndOfFrame)的队列中注册这个IEnumerator,然后继续执行将它移回正常的Coroutine队列(参见事件执行顺序)。

所有这些事件都在统一主线程中执行。

因此,像您这样使用Coroutine是相当多余的。

要么简单地使用

代码语言:javascript
复制
public class BallCup: MonoBehaviour
{
    bool wait = false;
    public float respawnTerm = 3;
    public GameObject Ball;
    public Vector3 spawnpoint;
   
    // Yes indeed Start can be IEnumerator and Unity will run it as Coroutine automatically 
    IEnumerator Start()
    {
        spawnpoint = transform.position - new Vector3(0, 0, 10);

        while(true)
        {
            Instantiate(Ball, spawnpoint, Quaternion.identity);
            yield return new WaitForSeconds(respawnTerm);
        }
    }
}

或者如果您喜欢在Update中不使用Coroutine就这样做

代码语言:javascript
复制
public class BallCup: MonoBehaviour
{
    bool wait = false;
    public float respawnTerm = 3;
    public GameObject Ball;
    public Vector3 spawnpoint;

    private float timer:
    
    void Start()
    {
        spawnpoint = transform.position - new Vector3(0, 0, 10);
    }

    private void Update ()
    {
        timer -= Time.deltaTime;

        if(timer <= 0f)
        {
            Instantiate(Ball, spawnpoint, Quaternion.identity);
           
            timer = respawnTerm: 
        }
    }
}

或者更简单地使用InvokeRepeating和do

代码语言:javascript
复制
public class BallCup: MonoBehaviour
{
    bool wait = false;
    public float respawnTerm = 3;
    public GameObject Ball;
    public Vector3 spawnpoint;
    
    void Start()
    {
        spawnpoint = transform.position - new Vector3(0, 0, 10);

        InvokeRepeating (nameof (SpawnBall), 0f, respawnTerm);
    }

    private void SpawnBall ()
    {
        Instantiate(Ball, spawnpoint, Quaternion.identity);
    }
}

所以对于你的问题

是那种投票吗?

不,不是真的。您根本不必担心同步,因为Unity已经为您做了这件事。当然,在内部,它可能被认为是“轮询的类型”,因为如果时间已经过去了,您可以检查一次帧(但是,这也可能是完全不同的处理方式--我们没有源代码)。“这类轮询”可能指的是

代码语言:javascript
复制
while(!someCondition)
{
    Thread.Sleep(50);
}

甚至更糟

代码语言:javascript
复制
while(! someCondition) { }

应该避免这样做。

当然,您是如何使用它的,正如前面提到的,它是一种冗余的轮询实现I Update,但是,除了非常冗余之外,您不必担心性能问题。

如果我不想每次调用createBallRegularly()的话。有什么方法可以像java线程那样实现吗?

好的,如上面所示,有多种更好的方法来实现您的行为,这些方法在Update中不使用标志轮询。

我听说这个任务与线程类似,那么使用'startcoroutine‘以获得更好的性能会更好吗?

如前所述,Coroutines既不是Task,也不是Thread

一般的问题是何时使用Task和何时使用Thread是一个棘手的问题。作为一种拇指规则,我认为Task通常只需要很短的时间,或者是“一次的事情”。另一方面,如果某物的运行时间较长,则应首选Thread。一些定期收到工作包的工作线程。两者各有优缺点。

票数 1
EN

Stack Overflow用户

发布于 2021-12-31 11:48:50

我真的不知道你想要满足什么条件,但有几种方法可以让这件事变得更好。

如果您想在球被销毁时启动协同线,则需要使void createBallRegularly()公开,并在Start()中调用此方法,然后每次实例化球被销毁时。

如果不只是删除bool,那么在start方法中调用coroutine,让它继续运行,而无需在更新中调用它。

票数 0
EN

Stack Overflow用户

发布于 2021-12-31 03:44:55

你读过吗?它有例子。

其他要注意的事项:

  1. Coroutine在主线程中运行,如果使用不当,将阻止进程。
  2. StartCoroutine导致类被实例化,多次使用它代价高昂,选择"while“或"for”替换
票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70538568

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档