在主线程中启动协程

Starting Coroutine in Main Thread

所以我想执行一个UnityWebrequest,它需要从Coroutine开始。 现在尝试在某个时候调试所有内容我发现从 Start 函数调用 Coroutine 是有效的(接收 Debug.Log 消息),但是从普通函数调用它它将不起作用(我没有收到调试消息) GetRequest).

因此我认为由于某种原因协程没有运行在主线程上运行(因为也没有出现错误)。

有人知道我如何在主线程上强制执行协程 运行 或解决我的问题的其他方法吗?

在这里您会找到代码,其中 WebGet 从不在 Monobehaviour 中的 class 调用,但包含 WebGet & GetRequest 的 class 是。

(请忽略 WebGet 中的请求变量,这将在稍后阶段使用。

 public UnityWebRequest WebGet(string url)
        {
            Debug.Log("This is the URL Get " + url);
            var request = UnityWebRequest.Get(url);

            Debug.Log("Request URL: " + request.url);

            StartCoroutine(GetRequest(url));

            Debug.Log("After Coroutine");

            return request;
        }

private IEnumerator GetRequest(string url)
        {
            var request = UnityWebRequest.Get(url);

            Debug.Log("This is the URL inside coroutine " + url);

            yield return request.SendWebRequest();

            if (request.isNetworkError)
            {
                Debug.Log("Error While Sending: " + request.error);
            }
            else
            {
                Debug.Log("Received: " + request.result);
            }

        }

大部分UnityAPI只能在Unitymain-thread上使用,StartCoroutine就是其中之一。

or another solution to my issue

如果您已经在后台线程上,您也可以简单地使用 UnityWebRequest.Get 而不使用任何协程,然后等待使用例如

request.SendWebRequest(); 
while(!request.isDone)
{ 
    // depends on your use case, but in general for the love of your CPU
    // you do want to wait a specific amount that is "ok" as a delay
    // you could of curse reduce this to approximately frame wise check by using e.g. 17 ms (=> checks 60 times per second)
    Thread.Sleep(50); // checks 20 times per second
}

if (request.isNetworkError)
{
    Debug.Log("Error While Sending: " + request.error);
}
else
{
    Debug.Log("Received: " + request.result);
}

请记住,您仍在后台线程中,此时可能不允许其他 Unity API 调用。


如果您的方法通常不在后台线程上,请注意 StartCoroutine 不会 NOT 延迟调用它的方法(这正是我们想要避免的通过使用协同程序),而是立即继续您的其余代码。

你当然可以这样做

var request = UnityWebRequest.Get(url);
request.SendWebRequest();
return request;

没有 yield 在任何协程之外对其进行处理,但是在您继续访问和使用结果之前,您需要确保 request.isDonetrue


How I can enforce a Coroutine to run on the main-thread?

为了强制在主线程上发生事情,您可以使用通常称为 Main thread dispatcher 的模式。简化版本可能类似于

public class MainThreadDispatcher : MonoBehaviour
{
    #region Singleton pattern (optional)
    private static MainThreadDispatcher _instance;
    public static MainThreadDispatcher Instance => _instance;

    private void Awake()
    {
        if(_instance && _instance != this)
        {
            Destroy(gameObject);
            return;
        }

        _instance = this;
        DontDestroyOnLoad(gameObject);
    }
    #endregion Singleton

    #region Dispatcher
    // You use a thread-safe collection first-in first-out so you can pass on callbacks between the threads
    private readonly ConcurrentQueue<Action> mainThreadActions = new ConcurrentQueue<Action>();
    
    // This now can be called from any thread/task etc
    // => dispatched action will be executed in the next Unity Update call
    public void DoInMainThread(Action action);
    {
        mainThreadActions.Enqueue(action);
    }
    
    // In the Unity main thread Update call routine you work off whatever has been enqueued since the last frame
    private void Update()
    {
        while(mainThreadActions.TryDequeue(out var action))
        {
            action?.Invoke();
        }
    }
    #endregion Dispatcher
}

然后在你的情况下你可以使用

//MainThreadDispatcher.Instance.DoInMainThread(() =>
yourMainThreadDispatcherReference.DoInMainThread(() =>
{
    StartCoroutine(GetRequest(url));
});